mORMot and Open Source friends
Check-in [1c6fe7a1fd]
Not logged in

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

Overview
SHA1:1c6fe7a1fd7fffade7c2f1494abbfff0140e3ec4
Date: 2012-11-28 13:34:11
User: abouchez
Comment:all former SQLite3\SQLite3*.pas units have been renamed to SQLite3\mORMot*.pas to match the database-agnostic scheme of the mORMot framework - this is a major break change, so all your "uses" clauses in your code is to be change to follow the new naming
Tags And Properties
Context
2012-11-28
13:35
[c0cd24feea] deleted whole ZEOS sub-folder from the repository, since it is not to be used any more, but our more efficient SynDB*.pas units (user: abouchez, tags: trunk)
13:34
[1c6fe7a1fd] all former SQLite3\SQLite3*.pas units have been renamed to SQLite3\mORMot*.pas to match the database-agnostic scheme of the mORMot framework - this is a major break change, so all your "uses" clauses in your code is to be change to follow the new naming (user: abouchez, tags: trunk)
2012-11-26
09:23
[73c7d1ce94] fixed issue in TSQLRestServerStaticExternal.EngineDeleteWhere() when calling commands like MyDB.Delete(TSQLMyClass, 'PLU < ?', [20000]) (user: abouchez, tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to PasZip.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/// ZIP/LZ77 Deflate/Inflate Compression in pure pascal
// - this unit is a part of the freeware Synopse framekork,
// licensed in the LGPL v3; version 1.5
unit PasZip;

{
    This file is part of Synopse SQLite3 database framework.

    Synopse SQLite3 database framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.

................................................................................
  Windows,
{$else}
  LibC, Types,
{$endif}
  SysUtils;

/// compress memory using the ZLib DEFLATE algorithm
function   CompressMem(src, dst: pointer; srcLen, dstLen: integer) : integer;

/// uncompress memory using the ZLib INFLATE algorithm
function UnCompressMem(src, dst: pointer; srcLen, dstLen: integer) : integer;

/// compress memory using the ZLib DEFLATE algorithm
function   CompressString(const data: string; failIfGrow: boolean = false) : string;

/// uncompress memory using the ZLib INFLATE algorithm
function UncompressString(const data: string) : string;


{$ifdef WIN32} { use Windows MapFile }
function   CompressFile(const srcFile, dstFile: string; failIfGrow: boolean = false) : boolean;
function UncompressFile(const srcFile, dstFile: string; lastWriteTime: int64 = 0; attr: dword = 0) : boolean;

function GetCompressedFileInfo   (const comprFile: string; var size: int64; var crc32: dword) : boolean;
function GetUncompressedFileInfo (const uncomprFile: string; var size: int64; var crc32: dword) : boolean;
function IsCompressedFileEqual   (const uncomprFile, comprFile: string) : boolean;

{{ You can create a "zip" compatible archive by calling the "Zip" function.

|
|



|

|







 







|





|






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/// ZIP/LZ77 Deflate/Inflate Compression in pure pascal
// - this unit is a part of the freeware Synopse framework,
// licensed in the LGPL v3; version 1.18
unit PasZip;

{
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.

................................................................................
  Windows,
{$else}
  LibC, Types,
{$endif}
  SysUtils;

/// compress memory using the ZLib DEFLATE algorithm
function CompressMem(src, dst: pointer; srcLen, dstLen: integer) : integer;

/// uncompress memory using the ZLib INFLATE algorithm
function UnCompressMem(src, dst: pointer; srcLen, dstLen: integer) : integer;

/// compress memory using the ZLib DEFLATE algorithm
function CompressString(const data: string; failIfGrow: boolean = false) : string;

/// uncompress memory using the ZLib INFLATE algorithm
function UncompressString(const data: string) : string;


{$ifdef WIN32} { use Windows MapFile }
function CompressFile(const srcFile, dstFile: string; failIfGrow: boolean = false) : boolean;
function UncompressFile(const srcFile, dstFile: string; lastWriteTime: int64 = 0; attr: dword = 0) : boolean;

function GetCompressedFileInfo   (const comprFile: string; var size: int64; var crc32: dword) : boolean;
function GetUncompressedFileInfo (const uncomprFile: string; var size: int64; var crc32: dword) : boolean;
function IsCompressedFileEqual   (const uncomprFile, comprFile: string) : boolean;

{{ You can create a "zip" compatible archive by calling the "Zip" function.

Changes to ReadMe.txt.

1
2
3
4
5
6
7
8
9
10



11
12
13
14
15
16
17
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
73
74
75
76
77
78
79

80

81
82
83

  Synopse mORMot framework

An Open Source Client-Server ORM/SOA framework
 (c) 2010-2012 Synopse Informatique
  http://synopse.info
  http://mormot.net


Synopse mORMot is a Client-Server ORM and Service Oriented Architecture framework for Delphi 6 up to XE3.




It provides an Open Source self-sufficient set of units (even Delphi stater is enough) for creating any business application, up to the most complex Domain-Driven design:

- Presentation layer featuring MVC UI generation with i18n and reporting for rich Delphi clients, or rich AJAX clients;
- Application layer implementing Service Oriented Architecture via interface-based services (like WCF) and Client-Server ORM - following a RESTful model using JSON over several communication protocols (including HTTP/1.1);
- Domain Model layer handling all the needed business logic in plain Delphi objects, including high-level managed types like dynamic arrays or records for Value Objects, or dedicated classes for entities or aggregates;
- Data persistence infrastructure layer with ORM persistence over Oracle, MS SQL, OleDB, with a powerful SQLite3 kernel;
................................................................................

And a blog is available:
http://blog.synopse.info


Don't forget to download the documentation (available in pdf files, created by our SynProject tool).
In particular, you should take a look at all general introduction chapters of the supplied SAD document. It will cover all key-concepts and code modeling used by the framework.
A developer guide is included in this SAD document, in its first part. You'll get good practice guidance, presentation of the ORM/SOA approach and other underlying concepts, and the multi-tier architecture.

Enjoy!


Quick steps to install mORMot:

0) Read the licenses at http://synopse.info/forum/viewtopic.php?id=27
................................................................................
5) Sample programs are found in: D:\Dev\Synopse\Sqlite3\Samples

6) Download the mORMot documentation from http://synopse.info/fossil/wiki?name=Downloads
   In particular, the SAD document is worth reading.

7) After having consulted both the documentation and the existing posts in the forum, feel free to ask your questions in the forum at http://synopse.info




Some units (like SynPdf, SynGdiPlus, SynBigTable, SynCommons, SynDB, SynSQLite3, SQLite3Pages) are used by mORMot, but do not require the whole framework to be used.
That is, you can use e.g. only the PDF generation features of SynPdf, the fast database access classes of SynDB, the static-linked SQLite3 engine of SynSQLite3, the code-generated reports of SQLite3Pages, or the TDynArray / TSynLog classes of SynCommons, without using the main mORMot classes and features (ORM, Client-Server, services, UI, reporting).
Some of those units can even be compiled with Delphi 5 (like SynPdf, SynDB or SynSQLite3).




|




|
>
>
>







 







|







 







>

>
|
|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
76
77
78
79
80
81
82
83
84
85
86
87
88

  Synopse mORMot framework

An Open Source Client-Server ORM/SOA framework
 (c) 2008-2012 Synopse Informatique
  http://synopse.info
  http://mormot.net


Synopse mORMot is a Client-Server ORM and Service Oriented Architecture framework for Delphi 6 up to XE3, featuring:
- Local or Client-Server ORM with support of in-memory data, SQLite3 or external SQL databases;
- Local or Client-Server interface-based services;
- A set of optimized general purpose routines and classes, including User Interface and Reporting.

It provides an Open Source self-sufficient set of units (even Delphi stater is enough) for creating any business application, up to the most complex Domain-Driven design:

- Presentation layer featuring MVC UI generation with i18n and reporting for rich Delphi clients, or rich AJAX clients;
- Application layer implementing Service Oriented Architecture via interface-based services (like WCF) and Client-Server ORM - following a RESTful model using JSON over several communication protocols (including HTTP/1.1);
- Domain Model layer handling all the needed business logic in plain Delphi objects, including high-level managed types like dynamic arrays or records for Value Objects, or dedicated classes for entities or aggregates;
- Data persistence infrastructure layer with ORM persistence over Oracle, MS SQL, OleDB, with a powerful SQLite3 kernel;
................................................................................

And a blog is available:
http://blog.synopse.info


Don't forget to download the documentation (available in pdf files, created by our SynProject tool).
In particular, you should take a look at all general introduction chapters of the supplied SAD document. It will cover all key-concepts and code modeling used by the framework.
A developer guide is included in this SAD document, in its 2nd part. You'll get good practice guidance, presentation of the ORM/SOA approach and other underlying concepts.

Enjoy!


Quick steps to install mORMot:

0) Read the licenses at http://synopse.info/forum/viewtopic.php?id=27
................................................................................
5) Sample programs are found in: D:\Dev\Synopse\Sqlite3\Samples

6) Download the mORMot documentation from http://synopse.info/fossil/wiki?name=Downloads
   In particular, the SAD document is worth reading.

7) After having consulted both the documentation and the existing posts in the forum, feel free to ask your questions in the forum at http://synopse.info

8) Feel free to contribute by posting enhancements and patches to this quickly evolving project.


Some units (like SynPdf, SynGdiPlus, SynBigTable, SynCommons, SynDB, SynSQLite3, mORMotReport) are used by mORMot, but do not require the whole framework to be used.
That is, you can use e.g. only the PDF generation features of SynPdf, the fast database access classes of SynDB, the static-linked SQLite3 engine of SynSQLite3, the code-generated reports of mORMotReport, or the TDynArray / TSynLog classes of SynCommons, without using the main mORMot classes and features (ORM, Client-Server, services, UI, reporting).
Some of those units can even be compiled with Delphi 5 (like SynPdf, SynDB or SynSQLite3).

Changes to SQLite3/Documentation/Release/ReleaseForm.pas.

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  procedure Search(const Folder: TFileName);
  var SR: TSearchRec;
  begin
    if FindFirst(Folder+'*.pas',faAnyFile,SR)<>0 then
      exit;
    repeat
      if IdemPChar(pointer(SR.Name),'SYN') or
         IdemPChar(pointer(SR.Name),'SQLITE3') then
        ProcessFile(Folder+SR.Name);
    until FindNext(SR)<>0;
    FindClose(SR);
  end;
begin
  SearchVersion := Edit1.Text;
  Search(ExtractFilePath(paramstr(0))+'..\..\..\'); // D:\Dev\Lib







|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  procedure Search(const Folder: TFileName);
  var SR: TSearchRec;
  begin
    if FindFirst(Folder+'*.pas',faAnyFile,SR)<>0 then
      exit;
    repeat
      if IdemPChar(pointer(SR.Name),'SYN') or
         IdemPChar(pointer(SR.Name),'MORMOT') then
        ProcessFile(Folder+SR.Name);
    until FindNext(SR)<>0;
    FindClose(SR);
  end;
begin
  SearchVersion := Edit1.Text;
  Search(ExtractFilePath(paramstr(0))+'..\..\..\'); // D:\Dev\Lib

Changes to SQLite3/Documentation/Synopse SQLite3 Framework.pro.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
...
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
....
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
....
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
....
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
....
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
....
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
....
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
....
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
....
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
....
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
....
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
....
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
....
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
....
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
....
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
....
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
....
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
....
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
....
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
....
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
....
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
....
3651
3652
3653
3654
3655
3656
3657
3658

3659
3660
3661
3662
3663
3664
3665
....
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
....
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
....
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
....
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
....
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
....
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
....
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
....
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
....
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
....
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
....
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
....
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
....
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
....
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
....
6245
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
6256
6257
6258
6259
....
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
....
6321
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
....
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
....
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
....
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
....
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
....
6825
6826
6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840
6841
6842
6843
6844
6845
....
6850
6851
6852
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867
6868
6869
6870
6871
6872
6873
6874
6875
6876
6877
6878
6879
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
....
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
....
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
7147
7148
7149
7150
7151
....
7205
7206
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
....
7229
7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
....
7393
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
....
7430
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
NoConfidential=Yes
; so that no "Confidential" text will appear in page footer - seems convenient for a GPL document ;)
HeaderWithLogo=Yes
; custom page header with the synopse logo

{\b Document License}
THE ATTACHED DOCUMENTS DESCRIBE INFORMATION RELEASED BY SYNOPSE INFORMATIQUE UNDER A GPL 3.0 LICENSE.
{\i Synopse SQLite3/mORMot Framework Documentation}.\line Copyright (C) 2008-2012 Arnaud Bouchez.\line Synopse Informatique - @http://synopse.info
This document is free document; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
The {\i Synopse mORMot Framework Documentation} is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this documentation. If not, see @http://www.gnu.org/licenses
{\b Trademark Notice}
Rather than indicating every occurrence of a trademarked name as such, this document uses the names only in an editorial fashion and to the benefit of the trademark owner with no intention of infringement of the trademark.

[Pictures]
................................................................................
;In the following sections, the {\i Synopse mORMot Framework} library architecture is detailed as:
;\SourcePage

[SAD-Main]
SourcePath=Lib\SQLite3
IncludePath=Lib;Lib\SQLite3
;Lib\SQLite3\Samples\MainDemo
SourceFile=TestSQL3.dpr;SQLite3i18n.pas;SQLite3ToolBar.pas;SQLite3UI.pas;SQLite3UILogin.pas;SQLite3Pages.pas;SQLite3UIOptions.pas;SQLite3UIQuery.pas;SQLite3Service.pas;SQLite3.pas;SQLite3HttpClient.pas;SQLite3HttpServer.pas;SynSQLite3.pas;SynDB.pas;SynOleDB.pas;SynDBOracle.pas;SynDBSQLite3.pas;SynDBODBC.pas
;Samples\MainDemo\SynFile.dpr
Version=1.18
DisplayName=mORMot Framework

:3Object-Relational Mapping
In order to implement @13@ in our framework, generic access to the data is implemented by defining high-level objects as {\i Delphi} classes, descendant from the {\f1\fs20 @**TSQLRecord@} class.
In our @*Client-Server@ @**ORM@, those classes can be used for at least three main purposes:
................................................................................
!    property SerialNumber: RawUTF8 index 30 read fSerialNumber write fSerialNumber
!!     stored AS_UNIQUE;
!    property Model: TSQLDiaperModel read fModel write fModel;
!    property Baby: TSQLBaby read fBaby write fBaby;
!end;
:  Text fields
Note that {\f1\fs20 WideString, shortstring, UnicodeString} (i.e. Delphi 2009/2010/XE/XE2/XE3 generic string), and indexed properties are not handled yet (use faster {\f1\fs20 RawUnicodeString} instead of {\f1\fs20 WideString} or {\f1\fs20 UnicodeString}). In fact, the generic {\f1\fs20 string} type is handled (as {\f1\fs20 UnicodeString} under Delphi 2009/2010/XE/XE2/XE3), but you may loose some content if you're working with pre-Unicode version of Delphi (in which {\f1\fs20 string = AnsiString} with the current system code page). So we won't recommend its usage.
The natural Delphi type to be used for TEXT storage in our framework is {\f1\fs20 @**RawUTF8@}. All business process should be made using {\f1\fs20 RawUTF8} variables and methods (you have all necessary functions in {\f1\fs20 SynCommons.pas}), then you should explicitly convert the {\f1\fs20 RawUTF8} content into a string using {\f1\fs20 U2S / S2U} from {\f1\fs20 SQlite3i18n.pas} or {\f1\fs20 StringToUTF8 / UTF8ToString} which will handle proper char-set conversion according to the current @*i18n@ settings.
For additional information about @*UTF-8@ handling in the framework, see @32@.
:  Date and time fields
For {\f1\fs20 @**TTimeLog@ / @**TModTime@ / @**TCreateTime@}, the proprietary fast {\f1\fs20 Int64} date time format will map the {\f1\fs20 Iso8601} record type, as defined in {\f1\fs20 SynCommons}:
- 0..5 bits will map seconds,
- 6..11 bits will map minutes,
- 12..16 bits will map hours,
- 17..21 bits will map days (minus one),
................................................................................
!  TSQLDest = class(TSQLRecordSigned)
!  published
!    property SignatureTime;
!    property Signature;
!  end;
When a {\f1\fs20 TSQLRecordMany} published property exists in a {\f1\fs20 TSQLRecord}, it is initialized automatically during {\f1\fs20 TSQLRecord.Create} constructor execution into a real class instance. Note that the default behavior for a {\f1\fs20 TSQLRecord} published property is to contain an {\f1\fs20 INTEGER} value which is the ID of the corresponding record - creating a "one to one" or "many to one" relationship. But {\f1\fs20 TSQLRecordMany} is a special case. So don't be confused! :)
This {\f1\fs20 TSQLRecordMany} instance is indeed available to access directly the pivot table records, via {\f1\fs20 FillMany} then {\f1\fs20 FillRow, FillOne} and {\f1\fs20 FillRewind} methods to loop through records, or {\f1\fs20 FillManyFromDest} / {\f1\fs20 DestGetJoined} for most advanced usage.
Here is how the regression @*test@s are written in the {\f1\fs20 SQLite3} unit:
!procedure TestMany(aClient: TSQLRestClient);
!var MS: TSQLSource;
!    MD, MD2: TSQLDest;
!    i: integer;
!    sID, dID: array[1..100] of Integer;
!    res: TIntegerDynArray;
!begin
................................................................................
!    Check(MS.DestList.Dest.SignatureTime=MD.fSignatureTime);
!    Check(MS.DestList.Dest.Signature=FormatUTF8('% %',[aClient.ClassName,i]));
!  end;
!!  MS.FillClose;
Note that in our case, an explicit call to {\f1\fs20 FillClose} has been added in order to release all {\f1\fs20 Dest} instances created in {\f1\fs20 FillPrepareMany}. This call is not mandatory if you call {\f1\fs20 MS.Free} directly, but it is required if the same {\f1\fs20 MS} instance is about to use some regular many-to-many methods, like {\f1\fs20 MS.DestList.ManySelect()} - it will prevent any GPF exception to occur with code expecting the {\f1\fs20 Dest} property not to be an instance, but a {\f1\fs20 pointer(DestID)} value.
: Calculated fields
It is often useful to handle some calculated fields. That is, having some field values computed when you set another field value. For instance, if you set an error code from an enumeration (stored in an INTEGER field), you may want the corresponding text (to be stored on a TEXT field). Or you may want a total amount to be computed automatically from some detailed records.
This should not be done on the Server side. In fact, the framework expects the transmitted JSON transmitted from client to be set directly to the database layer, as stated by this code from the {\f1\fs20 SQLite3} unit:
!function TSQLRestServerDB.EngineUpdate(Table: TSQLRecordClass; ID: integer;
!  const SentData: RawUTF8): boolean;
!begin
!  if (self=nil) or (Table=nil) or (ID<=0) then
!    result := false else begin
!    // this SQL statement use :(inlined params): for all values
!    result := EngineExecuteFmt('UPDATE % SET % WHERE RowID=:(%):;',
................................................................................
- Don't think "@*SQL@", think about classes;
- Don't wonder "How will I store it", but "Which data do I need".
For instance, don't be tempted to always create a pivot table (via a {\f1\fs20 @*TSQLRecordMany@} property), but consider using a {\i @*dynamic array@}, {\f1\fs20 @*TPersistent@, @*TStrings@} or {\f1\fs20 @*TCollection@} @*published properties@ instead.
Or consider that you can use a {\f1\fs20 TRecordReference} property pointing to any registered class of the {\f1\fs20 @*TSQLModel@}, instead of creating one {\f1\fs20 @*TSQLRecord@} property per potential table.
:  Objects, not tables
With an @*ORM@, you should usually define fewer tables than in a "regular" relational database, because you can use the high-level type of the {\f1\fs20 @*TSQLRecord@} properties to handle some per-row data.
The first point, which may be shocking for a database architect, is that you should better {\ul not} create @*Master/Detail@ tables, but just one "master" object with the details stored within, as @*JSON@, via {\i @*dynamic array@}, {\f1\fs20 @*TPersistent@, @*TStrings@} or {\f1\fs20 @*TCollection@} properties.
Another point is that a table is not to be created for every aspect of your software configuration. Let's confess that some DB architects design one configuration table per module or per data table. In an ORM, you could design a configuration class, then use the unique corresponding table to store all configuration encoded as some JSON data, or some DFM-like data. And do not hesitate to separate the configuration from the data, for all not data-related configuration - see e.g. how the {\f1\fs20 SQLite3Options} unit works. With our framework, you can serialize directly any {\f1\fs20 @*TSQLRecord@} or {\f1\fs20 TPersistent} instance into JSON, without the need of adding this {\f1\fs20 TSQLRecord} to the {\f1\fs20 @*TSQLModel@} list. Since revision 1.13 of the framework, you can even define {\f1\fs20 TPersistent} @published properties@ in your {\f1\fs20 TSQLRecord} class, and it will be automatically serialized as TEXT in the database.
:  Methods, not SQL
At first, you should be tempted to write code as such (this code sample was posted on our forum, and is not bad code, just not using the @*ORM@ orientation of the framework):
!  DrivesModel := CreateDrivesModel();
!  GlobalClient := TSQLRestClientDB.Create(DrivesModel, CreateDrivesModel(), 'drives.sqlite', TSQLRestServerDB);
!  TSQLRestClientDB(GlobalClient).Server.DB.Execute(
!    'CREATE TABLE IF NOT EXISTS drives ' +
!    '(id INTEGER PRIMARY KEY, drive TEXT NOT NULL UNIQUE COLLATE NOCASE);');
................................................................................
! begin
!!  GlobalClients.OneFieldValues(TSQLDrives,'drive','',aList);
! end;
The whole query is made in one line, with no {\f1\fs20 SELECT} statement to write.
For a particular ID range, you may have written, with a specific WHERE clause using a @*prepared@ statement:
!!  GlobalClients.OneFieldValues(TSQLDrives,'drive',
!!    'ID>=? AND ID<=?',[],[aFirstID,aLastID],aList);
It's certainly worth reading all the (verbose) interface part of the {\f1\fs20 SQLite3Commons.pas} unit, e.g. the {\f1\fs20 TSQLRest} class, to make your own idea about all the high-level methods available. In the following pages, you'll find all needed documentation about this particular unit. Since our framework is used in real applications, most useful methods should already have been made available. If you need additional high-level features, feel free to ask for them, if possible with source code sample, in our forum, freely available at @http://synopse.info
:  Think multi-tier
And do not forget the framework is able to have several level of objects, thanks to our @*Client-Server@ architecture - see @6@. Such usage is not only possible, but strongly encouraged.
You should have business-logic level objects at the Client side. Then both business-logic and DB objects at the Server side.
If you have a very specific database schema, business-logic objects can be of very high level, encapsulating some @*SQL@ views for reading, and accessed via some @*REST@ful @*service@ commands for writing - see @11@.
Another possibility to access your high-level type, is to use either custom {\i SQLite3} @*SQL function@s either @*stored procedure@s - see @23@ - both coded in Delphi.
:38 ORM Cache
Here is the definition of "cache", as stated by {\i Wikipedia}:
................................................................................
In order to make this easy, a dedicated set of classes are available in the {\f1\fs20 SynCommons.pas} unit, and allow to define both filtering and validation. They all will be children of any of those both classes:
\graph HierTSynFilter Filtering and Validation classes hierarchy
\TSynValidate\TSynFilterOrValidate
\TSynFilter\TSynFilterOrValidate
\
{\f1\fs20 @*TSQLRecord@} field content validation is handled in the new {\f1\fs20 TSQLRecord. Validate} virtual method, or via some {\f1\fs20 TSQLValidate} classes.
{\f1\fs20 TSQLRecord} field content filtering is handled in the new {\f1\fs20 TSQLRecord. Filter} virtual method, or via some {\f1\fs20 TSQLFilter} classes.
Some "standard" classes are already defined in the {\f1\fs20 SynCommons} and {\f1\fs20 SQLite3Commons} unit, to be used for most common usage:
\graph HierTSynFilters Default filters and Validation classes hierarchy
\TSynValidatePassWord\TSynValidateText
\TSynValidateText\TSynValidate
\TSynValidateTableUniqueField\TSynValidateTable
\TSynValidateTable\TSynValidate
\TSynValidateUniqueField\TSynValidateRest
\TSynValidateRest\TSynValidate
................................................................................
\TSynFilterLowerCaseU\TSynFilter
\TSynFilterLowerCase\TSynFilter
\TSynFilter\TSynFilterOrValidate
rankdir=LR;
\
You have powerful validation classes for IP Address, Email (with TLD+domain name), simple {\i regex} pattern, textual validation, strong password validation...
Note that some database-related filtering are existing, like {\f1\fs20 TSynValidateUniqueField} which inherits from {\f1\fs20 TSynValidateRest}.
Of course, the {\f1\fs20 SQLite3UIEdit} unit now handles {\f1\fs20 @*TSQLRecord@} automated filtering (using {\f1\fs20 TSQLFilter} classes) and validation (using one of the {\f1\fs20 TSQLValidate} classes).
The unique field validation is now in {\f1\fs20 TSQLRecord. Validate} and not in {\f1\fs20 SQLite3UIEdit} itself (to have a better multi-tier architecture).
To initialize it, you can add some filters/validators to your {\f1\fs20 @*TSQLModel@} creation function:
!function CreateFileModel(Owner: TSQLRest): TSQLModel;
!var Classes: array[0..high(FileTabs)] of TSQLRecordClass;
!    i: integer;
!begin
!  for i := 0 to high(FileTabs) do
!    Classes[i] := FileTabs[i].Table;
................................................................................
!  result.Owner := Owner;
!  result.SetActions(TypeInfo(TFileAction));
!  result.SetEvents(TypeInfo(TFileEvent));
!  TSQLFile.AddFilterOrValidate('Name',TSQLFilterLowerCase);
!  TSQLUser.AddFilterOrValidate('Email',TSQLValidateEmail);
!end;
In order to perform the filtering of some content, you'll have to call the {\f1\fs20 aRecord.Filter()} method, and {\f1\fs20 aRecord.Validate()} to test for valid content.
For instance, this is how {\f1\fs20 SQLite3UIEdit.pas} unit filters and validates the user interface input:
!procedure TRecordEditForm.BtnSaveClick(Sender: TObject);
! (...)
!  // perform all registered filtering
!!  Rec.Filter(ModifiedFields);
!  // perform content validation
!  FieldIndex := -1;
!!  ErrMsg := Rec.Validate(Client,ModifiedFields,@FieldIndex);
................................................................................
- {\f1\fs20 TSQLDatabase} can cache the last results for SELECT statements, or use a tuned client-side or server-side per-record caching, in order to speed up most read queries, for lighter web server or client User Interface e.g.;
- User @*authentication@ handling ({\i SQLite3} is user-free designed);
- {\i SQLite3} source code was compiled without thread mutex: the caller has to be @*thread-safe@ aware; this is faster on most configurations, since mutex has to be acquired once): low level {\f1\fs20 sqlite3_*()} functions are not thread-safe, as {\f1\fs20 TSQLRequest} and {\f1\fs20 TSQLBlobStream} which just wrap them; but {\f1\fs20 TSQLDataBase} is thread-safe, as {\f1\fs20 TSQLTableDB}/{\f1\fs20 TSQLRestServerDB}/{\f1\fs20 TSQLRestClientDB} which call {\f1\fs20 TSQLDataBase};
- Compiled with {\f1\fs20 SQLITE_OMIT_SHARED_CACHE} define, since with the new @*Client-Server@ approach of this framework, no concurrent access could happen, and an internal efficient caching algorithm is added, avoiding most call of the {\i SQLite3} engine in multi-user environment (any @*AJAX@ usage should benefit of it);
- The embedded {\i SQLite3} database engine can be easily updated from the official {\i SQLite3} source code available at @http://sqlite.org - use the amalgamation C file with a few minor changes (documented in the {\f1\fs20 SynSQLite3.pas} unit) - the resulting C source code delivered as {\f1\fs20 .obj} is also available in the official {\i Synopse} source code repository.
The overhead of including {\i SQlite3} in your server application will be worth it: just some KB to the executable, but with so many nice features, even if only external databases are used.
:  Extended by SQLite3 virtual tables
Since the framework is truly object oriented, another database engine could be used instead of the framework. You could easily write your own {\f1\fs20 TSQLRestServer} descendant (as an example, we included a fast in-memory database engine as {\f1\fs20 @*TSQLRestServerFullMemory@}) and link to a another engine (like {\i FireBird}, or a private one). You can even use our framework without any link to the {\i @*SQLite3@} engine itself, via our provided very fast in memory dataset (which can be made persistent by writing and reading @*JSON@ files on disk). The {\i SQLite3} engine is implemented in a separate unit, named {\f1\fs20 SynSQLite3.pas}, and the main unit of the framework is {\f1\fs20 SQLite3Commons.pas}. A bridge between the two units is made with {\f1\fs20 SQLite3.pas}, which will found our ORM framework using {\i SQLite3} as its core.
The framework ORM is able to access any database class (internal or external), via the powerful {\i SQLite3} Virtual Table mechanisms - see @20@. For instance, any external database (via @*OleDB@ / @*ODBC@ providers or direct {\i @*Oracle@} connection) can be accessed via our {\f1\fs20 @*SynDB@}-based dedicated units, as stated @27@.
As a result, the framework has several potential database back-ends, in addition to the default {\i SQLite3} file-based engine. Each engine may have its own purpose, according to the application expectations.
:59  Data access benchmark
On an average desktop computer, depending on the backend database interfaced, {\i mORMot} excels in speed:
- You can persist up to 150,000 objects per second, or retrieve  240,000 objects per second (for our pure Delphi in-memory engine);
- When data is retrieved from server or client @38@, you can read 450,000 objects per second;
- With a high-performance database like Oracle and our direct access classes, you can write 53,000 and read 72,000 objects per second, over a 100 MB network.
................................................................................
|{\b All Virtual}|167095|162956|168651|253292|118203|97083|90592|94688|56639|52764
|{\b All Direct}|167123|144250|168577|254284|256383|170794|165601|168856|88342|75999
|%
When declared as virtual table (via a {\f1\fs20 VirtualTableRegister} call), you have the full power of SQL (including JOINs) at hand, with incredibly fast CRUD operations: 100,000 requests per second for objects read and write, including serialization and Client-Server communication!
In the above list, the {\i MS SQL Server} is not integrated, but may be used instead of {\i Oracle} (minus the fact that BULK insert is not implemented yet for it, whereas @*array bind@ing boosts {\i Oracle} writing BATCH process performance by 100 times). Any other @*OleDB@ or @*ODBC@ providers may also be used, with direct access (without {\i DBExpress} / {\i BDE} layer nor heavy {\f1\fs20 TDataSet} instance).
Note that all those tests were performed locally and in-process, via a {\f1\fs20 TSQLRestClientDB} instance. For both insertion and reading, a Client-Server architecture (e.g. using HTTP/1.1 for {\i mORMot} clients) will give even better results for BATCH and retrieve all modes. During the tests, internal caching - see @37@ and @38@ - was disabled, so you may expect speed enhancements for real applications, when data is more read than written: for instance, when an object is retrieved from the cache, you achieve 450,000 read requests per second, whatever database is used.
: SQLite3 implementation
Beginning with the revision 1.15 of the framework, the {\i @**SQLite3@} engine itself has been separated from our {\f1\fs20 SQLite3.pas} unit, and defined as a stand-alone unit named {\f1\fs20 SynSQLite3.pas}. See @SDD-DI-2.2.1@.
It can be used therefore:
- Either stand-alone with direct access of all its features, even using its lowest-level C API, via {\f1\fs20 SynSQLite3.pas} - but you won't be able to switch to another database engine easily;
- Either stand-alone with high-level SQL access, using our {\f1\fs20 @*SynDB@} generic access classes, via {\f1\fs20 SynDBSQLite3.pas} - so you will be able to change to any other database engine (e.g. MSSQL or @*Oracle@) when needed;
- Either Client-Server based access with all our @*ORM@ features - see {\f1\fs20 SQLite3.pas}.
We'll define here some highlights specific to our own implementation of the {\i SQLite3} engine, and let you consult the official documentation of this great Open Source project at @http://sqlite.org for general information about its common features.
:14  Prepared statement
In order to speed up the time spent in the {\i SQLite3} engine (it may be useful for high-end servers), the framework is able to natively handle @*prepared@ @*SQL@ statements.
Starting with version 1.12 of the framework, we added an internal SQL statement @*cache@ in the database access, available for all SQL request. Previously, only the one-record SQL {\f1\fs20 SELECT * FROM ... WHERE RowID=...} was prepared (used e.g. for the {\f1\fs20 @*TSQLRest@. Retrieve} method).
That is, if a previous SQL statement is run with some given parameters, a prepared version, available in cache, is used, and new parameters are bounded to it before the execution by {\i SQLite3}.
In some cases, it can speed the {\i SQLite3} process a lot. From our profiling, prepared statements make common requests (i.e. select / insert / update on one row) at least two times faster, on an in-memory database ({\f1\fs20 ':memory:'} specified as file name).
In order to use this statement caching, any SQL statements must have the parameters to be surrounded with '{\f1\fs20 :(}' and '{\f1\fs20 ):}'. The SQL format was indeed enhanced by adding an optional way of marking parameters inside the SQL request, to enforce statement preparing and caching.
................................................................................
will execute the following @*SQL@ statement:
$ SELECT MapData.ID From MapData, MapBox WHERE MapData.ID=MapBox.ID
$  AND minX>=:(-81.0): AND maxX<=:(-79.6): AND minY>=:(35.0): AND :(maxY<=36.2):
$  AND MapBox_in(MapData.BlobField,:('\uFFF0base64encoded-81,-79.6,35,36.2'):);
The {\f1\fs20 MapBox_in} @*SQL function@ is registered in {\f1\fs20 @*TSQLRestServerDB@. Create} constructor for all {\f1\fs20 TSQLRecordRTree} classes of the current database model. Both {\f1\fs20 BlobToCoord} and {\f1\fs20 ContainedIn} class methods are used to handle the box storage in the BLOB. By default, it will process a raw {\f1\fs20 array of double}, with a default box match (that is {\f1\fs20 ContainedIn} method will match the simple {\f1\fs20 minX>=...maxY<=...} where clause).
:8  FTS3/FTS4
@**FTS@3/FTS4 are {\i @*SQLite3@} @*virtual table@ modules that allow users to perform full-text searches on a set of documents. The most common (and effective) way to describe full-text searches is "what Google, Yahoo and Altavista do with documents placed on the World Wide Web". Users input a term, or series of terms, perhaps connected by a binary operator or grouped together into a phrase, and the full-text query system finds the set of documents that best matches those terms considering the operators and groupings the user has specified. See @http://www.sqlite.org/fts3.html as reference material about FTS3 usage in {\i SQLite3}.
Since version 1.5 of the framework, the {\f1\fs20 sqlite3fts3.obj} file is always available in the distribution file: just define the {\f1\fs20 INCLUDE_FTS3} conditional globaly for your application (it is expected e.g. in {\f1\fs20 SQLite3.pas} and {\f1\fs20 SynSQLite3.pas}) to enable {\i FTS3} in your application.
Leave it undefined if you do not need this feature, and will therefore spare some KB of code.
FTS3 and FTS4 are nearly identical. FTS4 is indeed an enhancement to FTS3, added with SQLite version 3.7.4, and included in the release v.1.11 of the framework. They share most of their code in common, and their interfaces are the same. The differences are:
- FTS4 contains query performance optimizations that may significantly improve the performance of full-text queries that contain terms that are very common (present in a large percentage of table rows).
- FTS4 supports some additional options that may used with the {\f1\fs20 matchinfo()} function.
Because it stores extra information on disk in two new shadow tables in order to support the performance optimizations and extra {\f1\fs20 matchinfo()} options, FTS4 tables may consume more disk space than the equivalent table created using FTS3. Usually the overhead is 1-2% or less, but may be as high as 10% if the documents stored in the FTS table are very small. The overhead may be reduced by using a {\f1\fs20 TSQLRecordFTS3} table type instead of {\f1\fs20 TSQLRecordFTS4} declaration, but this comes at the expense of sacrificing some of the extra supported {\f1\fs20 matchinfo()} options.
:   Dedicated FTS3/FTS4 record type
In order to allow easy use of the @*FTS@ feature, some types have been defined:
................................................................................
- One cannot run {\f1\fs20 ALTER TABLE ... ADD COLUMN} commands against a virtual table.
- Particular virtual table implementations might impose additional constraints. For example, some virtual implementations might provide read-only tables. Or some virtual table implementations might allow {\f1\fs20 INSERT} or {\f1\fs20 DELETE} but not {\f1\fs20 UPDATE}. Or some virtual table implementations might limit the kinds of {\f1\fs20 UPDATE}s that can be made.
Example of virtual tables, already included in the {\i SQLite3} engine, are @*FTS@ or @*RTREE@ tables.
Our framework introduces new types of custom virtual table. You'll find classes like {\f1\fs20 @*TSQLVirtualTableJSON@} or {\f1\fs20 @*TSQLVirtualTableBinary@} which handle in-memory data structures. Or it might represent a view of data on disk that is not in the {\i SQLite3} format (e.g. {\f1\fs20 TSQLVirtualTableLog}). It can be used to access any external database, just as if they were native {\i SQLite3} tables - see @27@. Or the application might compute the content of the virtual table on demand.
Thanks to the generic implementation of Virtual Table in {\i SQLite3}, you can use such tables in your SQL statement, and even safely execute a {\f1\fs20 SELECT} statement with {\f1\fs20 JOIN} or custom functions, mixing normal {\i SQLite3} tables and any other Virtual Table. From the @*ORM@ point of view, virtual tables are just tables, i.e. they inherit from {\f1\fs20 TSQLRecordVirtual}, which inherits from the common base {\f1\fs20 TSQLRecord} class.
:  Virtual Table module classes
A dedicated mechanism has been added to the framework, beginning with revision 1.13, in order to easily add such virtual tables with pure Delphi code.
In order to implement a new @*Virtual Table@ type, you'll have to define a so called {\i Module} to handle the fields and data access and an associated {\i Cursor} for the {\f1\fs20 SELECT} statements. This is implemented by the two {\f1\fs20 TSQLVirtualTable} and {\f1\fs20 TSQLVirtualTableCursor} classes as defined in the @!TSQLVirtualTable,TSQLVirtualTableCursor,TSQLVirtualTableJSON,TSQLVirtualTableBinary,TSQLVirtualTableLog,TSQLVirtualTableCursorLog,TSQLVirtualTableCursorJSON,TSQLVirtualTableCursorIndex!Lib\SQLite3\SQLite3Commons.pas@ unit.
For instance, here are the default Virtual Table classes deriving from those classes:
\graph HierTSQLVirtualTable Virtual Tables classes hierarchy
\TSQLVirtualTableJSON\TSQLVirtualTable
\TSQLVirtualTableBinary\TSQLVirtualTableJSON
\TSQLVirtualTableLog\TSQLVirtualTable
\TSQLVirtualTableCursorIndex\TSQLVirtualTableCursor
\TSQLVirtualTableCursorJSON\TSQLVirtualTableCursorIndex
\TSQLVirtualTableCursorLog\TSQLVirtualTableCursorIndex
\
{\f1\fs20 @*TSQLVirtualTableJSON@, @*TSQLVirtualTableBinary@} and {\f1\fs20 TSQLVirtualTableCursorJSON} classes will implement a Virtual Table using a {\f1\fs20 @*TSQLRestServerStaticInMemory@} instance to handle fast in-memory @*static@ databases. Disk storage will be encoded either as UTF-8 @*JSON@ (for the {\f1\fs20 TSQLVirtualTableJSON} class, i.e. the '{\f1\fs20 JSON}' module), either in a proprietary @*SynLZ@ compressed format (for the {\f1\fs20 TSQLVirtualTableBinary} class, i.e. the '{\f1\fs20 Binary}' module). File extension on disk will be simply {\f1\fs20 .json} for the '{\f1\fs20 JSON}' module, and {\f1\fs20 .data} for the '{\f1\fs20 Binary}' module. Just to mention the size on disk difference, the 502 KB {\f1\fs20 People.json} content (as created by included regression tests) is stored into a 92 KB {\f1\fs20 People.data} file, in our proprietary optimized format.
Note that the virtual table module name is retrieved from the class name. For instance, the {\f1\fs20 TSQLVirtualTableJSON} class will have its module named as 'JSON' in the SQL code.
To handle external databases, two dedicated classes, named {\f1\fs20 TSQLVirtualTableExternal} and {\f1\fs20 TSQLVirtualTableCursorExternal} will be defined in a similar manner - see @%%HierExternalTables@ @30@.
As you probably have already stated, all those Virtual Table mechanism is implemented in {\f1\fs20 SQLite3Commons}. Therefore, it is independent from the {\i SQLite3} engine, even if, to my knowledge, there is no other SQL database engine around able to implement this pretty nice feature.
:  Defining a Virtual Table module
Here is how the {\f1\fs20 TSQLVirtualTableLog} class type is defined, which will implement a @*Virtual Table@ module named "{\f1\fs20 Log}". Adding a new module is just made by overriding some Delphi methods:
!  TSQLVirtualTableLog = class(TSQLVirtualTable)
!  protected
!    fLogFile: TSynLogFile;
!  public
!    class procedure GetTableModuleProperties(
................................................................................
- {\f1\fs20 TQuery} {\i emulation class}, for direct re-use with existing code, in replacement to the deprecated BDE technology;
- Free {\f1\fs20 @*SynDBExplorer@} tool provided, which is a small but efficient way of running queries in a simple User Interface, about all available engines; it is also a good sample program of a stand-alone usage of those libraries.
:   Data types
Of course, our ORM does not need a whole feature set (do not expect to use this database classes with your VCL DB RAD components), but handles directly the basic SQL column types, as needed by our ORM (derived from SQLite's internal column types): {\f1\fs20 NULL, Int64, Double, @*Currency@, DateTime, @*RawUTF8@} and {\f1\fs20 BLOB}.
They are defined as such in {\f1\fs20 @*SynDB@}:
!  TSQLDBFieldType =
!    (ftUnknown, ftNull, ftInt64, ftDouble, ftCurrency, ftDate, ftUTF8, ftBlob);
Those types will map low-level database-level access types, not high-level Delphi types as {\f1\fs20 TSQLFieldType} defined in {\f1\fs20 SQLite3Commons}, or the generic huge {\f1\fs20 TFieldType} as defined in the standard VCL {\f1\fs20 DB.pas} unit. In fact, it is more tied to the standard {\i @*SQLite3@} generic types, i.e. NULL, INTEGER, REAL, TEXT, BLOB (with the addition of a {\f1\fs20 ftCurrency} and {\f1\fs20 ftDate} type, for better support of most DB engines) see @http://www.sqlite.org/datatype3.html.
You can note that the only {\f1\fs20 string} type handled here uses UTF-8 encoding (implemented using our {\f1\fs20 RawUTF8} type), for cross-Delphi true Unicode process. Code can access to the textual data via {\f1\fs20 variant, string} or {\f1\fs20 widestring} variables and parameters, but our units will use UTF-8 encoding internally. It will therefore interface directly with our ORM, which uses the same encoding.
BLOB columns or parameters are accessed as {\f1\fs20 RawByteString} variables, which may be mapped to a standard {\f1\fs20 TStream} via our {\f1\fs20 TRawByteStringStream}.
:   SynDB Units
Here are the units implementing the external database-agnostic features:
|%30%70
|\b File|Description\b0
|{\f1\fs20 SynDB}|abstract database direct access classes
................................................................................
!  end;
!end;
As you can see, there is no difference with using the local {\i SQLite3} engine or a remote database engine. From the Client point of view, you just call the usual RESTful methods, i.e. {\f1\fs20 Add / Retrieve / Update / UnLock / Delete}, and you can even handle advanced methods like a {\f1\fs20 FillPrepare} with a complex WHERE clause, or {\f1\fs20 CreateSQLMultiIndex / CreateMissingTables} on the server side. Even the creation of the table in the remote database (the {\f1\fs20 'CREATE TABLE...'} SQL statement) is performed by the framework, with the appropriate column properties according to the database expectations (e.g. a {\f1\fs20 TEXT} for {\i SQLite3} will be a {\f1\fs20 NVARCHAR2} field for {\i Oracle}).
The only specific instruction is the global {\f1\fs20 @*VirtualTableExternalRegister@} function, which has to be run on the server side (it does not make any sense to run it on the client side, since for the client there is no difference between any tables - in short, the client do not care about storage; the server does). In order to work as expected, {\f1\fs20 VirtualTableExternalRegister()} shall be called {\i before} {\f1\fs20 TSQLRestServer.Create} constructor: when the server initializes, the ORM server must know whenever an {\i internal} or {\i external} database shall be managed.
Note that in the above code, the {\f1\fs20 LastChange} field was defined as a {\f1\fs20 TModTime}: in fact, the current date and time will be stored each time the record is updated, i.e. for each {\f1\fs20 fClient.Add} or {\f1\fs20 fClient.Update} calls. This is tested by both {\f1\fs20 RExt.LastChange>=Now} and {\f1\fs20 RExt.LastChange<=Now} checks in the latest loop. The time used is the "server-time", i.e. the current time and date on the server (not on the client), and, in the case of external databases, the time of the remote server (it will execute e.g. a {\f1\fs20 select getdate()} under @*MS SQL@ to synchronize the date to be inserted for {\f1\fs20 LastChange}). In order to retrieve this server-side time stamp, we use {\f1\fs20 Now := fClient.ServerTimeStamp} instead of the local {\f1\fs20 Iso8601Now} function.
A similar feature is tested for the {\f1\fs20 CreatedAt} published field, which was defined as {\f1\fs20 TCreateTime}: it will be set automatically to the current server time at record creation (and not changed on modifications). This is the purpose of the {\f1\fs20 RExt.CreatedAt<=Now} check in the above code.
:30   Behind the scene
The {\f1\fs20 SQLite3DB.pas} unit, introduced in revision 1.15, implements @*Virtual Table@s access for any {\f1\fs20 @*SynDB@}-based external database for the framework.
In fact, the new {\f1\fs20 TSQLRestServerStaticExternal, TSQLVirtualTableCursorExternal} and {\f1\fs20 TSQLVirtualTableExternal} classes will implement this feature:
\graph HierExternalTables External Databases classes hierarchy
\TSQLRecordVirtual\TSQLRecord
\TSQLRecordVirtualTableAutoID\TSQLRecordVirtual
\TSQLVirtualTableCursorExternal\TSQLVirtualTableCursor
\TSQLVirtualTableExternal\TSQLVirtualTable
\
................................................................................
!!  aDB.UseCache := true; // we better use caching in this JSON oriented use
!  (...)
This will enable a global JSON cache at the SQL level. This cache will be reset on every {\f1\fs20 INSERT, UPDATE} or {\f1\fs20 DELETE} SQL statement, whatever the corresponding table is.
In practice, this global cache was found to be efficient, even if its implementation is some kind of "naive". It is in fact much more tuned than other HTTP-level caching mechanisms used in most client-server solutions (using e.g. a {\i Squid} proxy) - since our caching is at the SQL level, it is shared among all @*CRUD@ / @*Rest@ful queries, and is also indenpendent from the authentication scheme, which pollutes the URI. Associated with the other levels of cache - see @38@ - the framework scaling was found to be very good.
:9  REST
:   RESTful implementation
{\i Representational state transfer} (@**REST@) is a style of software architecture for distributed hypermedia systems such as the World Wide Web. As such, it is not just a method for building "web @*service@s". The terms "representational state transfer" and "REST" were introduced in 2000 in the doctoral dissertation of Roy Fielding, one of the principal authors of the Hypertext Transfer Protocol (@**HTTP@) specification, on which the whole Internet rely.
The {\i Synopse mORMot Framework} was designed in accordance with Fielding's REST architectural style without using HTTP and without interacting with the World Wide Web. Such Systems which follow REST principles are often referred to as "RESTful". Optionally, the Framework is able to serve standard HTTP/1.1 pages over the Internet (by using the {\f1\fs20 SQLite3Http} unit and the {\f1\fs20 TSQLite3HttpServer} and {\f1\fs20 TSQLite3HttpClient} classes), in an embedded low resource and fast HTTP server.
The standard RESTful methods are implemented:
- GET to list the members of the collection;
- PUT to update a member of the collection;
- POST to create a new entry in the collection;
- DELETE to delete a member of the collection.
The following methods were added to the standard REST definition, for locking individual records and for handling database @*transaction@s (which speed up database process):
- LOCK to lock a member of the collection;
................................................................................
Some dedicated methods of the generic {\f1\fs20 @*TSQLRest@} class handle BLOB fields: {\f1\fs20 RetrieveBlob} and {\f1\fs20 UpdateBlob}.
:   REST and JSON
The "{\i 04 - @*HTTP@ @*Client-Server@}" sample application available in the framework source code tree can be used to show how the framework is @*AJAX@-ready, and can be proudly compared to any other @*REST@ server (like {\i CouchDB}) also based on {\f1\fs20 JSON}.
First desactivate the authentication - see @18@ - by changing the parameter from {\f1\fs20 true} to {\f1\fs20 false} in {\f1\fs20 Unit2.pas}:
! DB := TSQLRestServerDB.Create(Model,ChangeFileExt(paramstr(0),'.db3'),
!! false);
and by commenting the following line in {\f1\fs20 Project04Client.dpr}:
!  Form1.Database := TSQLite3HttpClient.Create(Server,'8080',Form1.Model);
!!  // TSQLite3HttpClient(Form1.Database).SetUser('User','synopse');
!  Application.Run;
Then you can use your browser to test the JSON content:
- Start the {\f1\fs20 Project04Server.exe} program: the background HTTP server, together with its {\i SQLite3} database engine;
- Start any {\f1\fs20 Project04Client.exe} instances, and add/find any entry, to populate the database a little;
- Close the {\f1\fs20 Project04Client.exe} programs, if you want;
- Open your browser, and type into the address bar:
$  http://localhost:8080/root
- You'll see an error message:
$TSQLite3HttpServer Server Error 400
- Type into the address bar:
$  http://localhost:8080/root/SampleRecord
- You'll see the result of all {\f1\fs20 SampleRecord} IDs, encoded as a JSON list, e.g.
$ [{"ID":1},{"ID":2},{"ID":3},{"ID":4}]
- Type into the address bar:
$  http://localhost:8080/root/SampleRecord/1
- You'll see the content of the {\f1\fs20 SampleRecord} of ID=1, encoded as JSON, e.g.
................................................................................
!begin
!  FParent := Value;
!end;
In Delphi, most common kind of reference-copy variables (i.e. {\f1\fs20 variant}, {\i dynamic array} or {\f1\fs20 string}) solve this issue by implementing {\i copy-on-write}. Unfortunately, this pattern is not applicable to {\f1\fs20 interface}, which are not value objects, but reference objects, tied to an implementation {\f1\fs20 class}, which can't be copied.
One common solution is to use {\i Weak pointers}, by which the {\f1\fs20 interface} is assigned to a property without incrementing the reference count.
Note that garbage collector based languages (like Java or C#) do not suffer from this problem, since the circular references are handled by their memory model: objects lifetime are maintained globally by the memory manager. Of course, it will increase memory use, slowdown the process due to additional actions during allocation and assignments (all objects and their references have to be maintained in internal lists), and may slow down the application when garbage collector enters in action. In order to avoid such issues when performance matters, experts tend to pre-allocate and re-use objects: this is one common limitation of this memory model, and why Delphi is still a good candidate (like unmanaged C or C++ - and also {\i Objective C}) when it deals with performance and stability. In some cases (e.g. when using an object cache), such languages have to introduce some kind of "weak pointers", to allow some referenced objects to be reclaimed by garbage collection: but it is a diverse mechanism, under the same naming.
:    Handling weak pointers
In order to easily create a weak pointer, the following function was added to {\f1\fs20 SQLite3Commons.pas}:
!procedure SetWeak(aInterfaceField: PIInterface; const aValue: IInterface);
!begin
!  PPointer(aInterfaceField)^ := Pointer(aValue);
!end;
It will assign the `interface` to a field by assigning the `pointer` of this instance to the internal field. It will by-pass the reference counting, so memory won't be leaked any more.
Therefore, it could be used as such:
!procedure TParent.SetChild(const Value: IChild);
................................................................................
!begin
!  SetWeak(@FParent,Value);
!end;
:    Zeroing weak pointers
But there are still some cases where it is not enough. Under normal circumstances, a {\f1\fs20 class} instance should not be deallocated if there are still outstanding references to it. But since weak references don't contribute to an {\f1\fs20 interface} reference count, a {\f1\fs20 class} instance can be released when there are outstanding weak references to it. Some memory leak or even random access violations could occur. A debugging nightmare...
In order to solve this issue, ARC's {\i Zeroing Weak pointers} come to mind.\line It means that weak references will be set to {\f1\fs20 nil} when the object they reference is released. When this happens, the automatic zeroing of the outstanding weak references prevents them from becoming dangling pointers. And {\i voilŕ}! No access violation any more!
Such a {\i Zeroing} ARC model has been implemented in {\i Objective C} by Apple, starting with Mac OS X 10.7 Lion, in replacement (and/or addition) to the previous manual memory handling implementation pattern: in its Apple's flavor, ARC is available not only for interfaces, but for objects, and is certainly more sophisticated than the basic implementation available in the Delphi compiler: it is told (at least from the marketing paper point of view) to use some deep knowledge of the software architecture to provide an accurate access to all instances - whereas the Delphi compiler just relies on a {\i out-of-scope} pattern. In regard to classic {\i garbage collector} memory model, ARC is told to be much more efficient, due to its deterministic nature: Apple's experts ensure that it does make a difference, in term of memory use and program latency - which both are very sensitive on "modest" mobile devices. In short, thanks to ARC, your phone UI won't glitch during background garbage recycling. So {\f1\fs20 mORMot} will try to offer a similar feature, even if the Delphi compiler does not implement it (yet).
In order to easily create a so-called zeroing weak pointer, the following function was defined in {\f1\fs20 SQLite3Commons.pas}:
!procedure SetWeakZero(aObject: TObject; aObjectInterfaceField: PIInterface;
!  const aValue: IInterface);
A potential use case could be:
!procedure TParent.SetChild(const Value: IChild);
!begin
!  SetWeakZero(self,@FChild,Value);
!end;
................................................................................
- {\i Andreas Hausladen} provided a classical and complete implementation at @http://andy.jgknet.de/blog/2009/06/weak-interface-references using some nice tricks (like per-instance optional speed up using a void {\f1\fs20 IWeakInterface interface} whose VMT slot will refer to the references list), is thread-safe and is compatible with most Delphi versions - but it will slow down all {\f1\fs20 TObject.FreeInstance} calls (i.e. within {\f1\fs20 Free / Destroy}) and won't allow any overriden {\f1\fs20 FreeInstance} method implementation;
- {\i Vincent Parrett} proposed at @http://www.finalbuilder.com/Resources/Blogs/PostId/410/WeakRefence-in-Delphi-solving-circular-interfac.aspx a {\f1\fs20 generic}-based solution (not thread-safe nor optimized for speed), but requiring to inherit from a base class for any {\f1\fs20 class} that can have a weak reference pointing to it;
- More recently, {\i Stefan Glienke} published at @http://delphisorcery.blogspot.fr/2012/06/weak-interface-references.html another {\f1\fs20 generic}-based solution, not requiring to inherit from a base class, but not thread-safe and suffering from the same limitations related to {\f1\fs20 TObject.FreeInstance}.
The implementation included within {\i mORMot} uses several genuine patterns, when compared to existing solutions:
- It will hack the {\f1\fs20 TObject.FreeInstance} at the {\f1\fs20 class} VMT level, so will only slow down the exact {\f1\fs20 class} which is used as a weak reference, and not others (also its inherited {\f1\fs20 classes} won't be overridden) - and it will allow custom override of the {\f1\fs20 virtual FreeInstance} method;
- It makes use of our {\f1\fs20 TDynArrayHashed} wrapper to provide a very fast lookup of instances and references, without using {\f1\fs20 generic} definitions - hashing will start when it will be worth it, i.e. for any list storing more than 32 items;
- The unused {\f1\fs20 vmtAutoTable} VMT slot is used to handle the class-specific orientation of this feature (similar to {\f1\fs20 TSQLRecordProperties} lookup as implemented for @DI-2.1.3@), for best speed and memory use.
See the {\f1\fs20 TSetWeakZeroClass} and {\f1\fs20 TSetWeakZeroInstance} implementation in {\f1\fs20 SQlite3Commons.pas} for the details.
:62   Interfaces in practice: dependency injection, stubs and mocks
In order to fulfill the @47@, two features are to be available when handling interfaces:
- Dependency injection;
- Stubbing and mocking of interfaces for proper testing.
We will show now how {\i mORMot} provides all needed features for such patterns, testing a simple "forgot my password" scenario: a password shall be computed for a given user name, then transmitted via SMS, and its record shall be updated in the database.
:    Dependency injection
A direct implementation of dependency injection at a {\f1\fs20 class} level can be implemented in Delphi as such:
................................................................................
- Named pipes, which can be used locally between a Server running as a Windows service and some Client instances;
- @*HTTP@/1.1 over TCP/IP, for remote access.
See @%%mORMotDesign1@ about this Client-Server architecture.
:  Implementation design
A typical Client-Server @*REST@ful POST / Add request over HTTP/1.1 will be implemented as such, on both Client and Server side:
\graph ArchClient Client-Server implementation - Client side
subgraph cluster {
\Client.Add\TSQLite3HttpClient.URI\ORM to JSON over REST
\TSQLite3HttpClient.URI\Http Server\HTTP protocol¤POST
\Http Server\TSQLite3HttpClient.URI
\TSQLite3HttpClient.URI\Client.Add\return¤new ID
label = "Client";
}
\
\graph ArchServer Client-Server implementation - Server side
subgraph cluster {
\Http Server\TSQLRestServer.URI\dispatch
\TSQLRestServer.URI\TSQLRestServerDB.EngineAdd\decode¤POST JSON
................................................................................
label = "Server";
}
\
Of course, several clients can access to the same server.
It's possible to by-pass the whole Client-Server architecture, and let the application be stand-alone, by defining a {\f1\fs20 @*TSQLRestClientDB@} class, which will embed a {\f1\fs20 @*TSQLRestServerDB@} instance in the same executable:
\graph ArchStandAlone Client-Server implementation - Stand-Alone application
subgraph cluster {
\Client.Add\TSQLite3HttpClientDB.URI\ORM to JSON over REST
\TSQLite3HttpClientDB.URI\TSQLRestServerDB.URI\direct call
\TSQLRestServerDB.URI\TSQLRestServerDB.EngineAdd\decode¤POST JSON
\TSQLRestServerDB.EngineAdd\SQLite3 SQL\SQL insert
\SQLite3 SQL\SQLite3 BTREE\prepare + execute
\SQLite3 BTREE\Database file\atomic write
\SQLite3 BTREE\TSQLRestServerDB.URI\return new ID
\TSQLRestServerDB.URI\Client.Add\return new ID
label = "Stand-Alone application";
................................................................................
\OleDB/ODBC or other\TSQLDBStatement
\TSQLDBStatement\TSQLVirtualTableExternal
\TSQLVirtualTableExternal\TSQLRestServer.URI\return new ID
\TSQLRestServer.URI\Http Server\return 200 OK + ID
label = "Server";
}
\
In fact, the above function correspond to a database model with only external virtual tables, and with {\f1\fs20 StaticVirtualTableDirect=false}, i.e. calling the Virtual Table mechanism of SQlite3 for each request.

Most of the time, i.e. for RESTful / @*CRUD@ commands, the execution is more direct:
\graph ArchVirtualDirect Client-Server implementation - Server side with "static" Virtual Tables
subgraph cluster {
\Http Server\TSQLRestServer.URI\dispatch
\TSQLRestServer.URI\TSQLRestServerDB.EngineAdd\Is a SQLite3 table?
\TSQLRestServer.URI\TSQLRestServerStaticExternal.EngineAdd\Is a static table?
\TSQLRestServerDB.EngineAdd\SQLite3 SQL\SQL insert
................................................................................
\TSQLRestServerRemoteDB\TSQLRestServer
\TSQLRestServerFullMemory\TSQLRestServer
\TSQLRestServer\TSQLRest
\TSQLRestClientURINamedPipe\TSQLRestClientURI
\TSQLRestClientURIMessage\TSQLRestClientURI
\TSQLRestClientURIDll\TSQLRestClientURI
\TSQLRestClientDB\TSQLRestClientURI
\TSQLite3HttpClientWinINet\TSQLite3HttpClientWinGeneric
\TSQLite3HttpClientWinHTTP\TSQLite3HttpClientWinGeneric
\TSQLite3HttpClientWinGeneric\TSQLite3HttpClientGeneric
\TSQLite3HttpClientWinSock\TSQLite3HttpClientGeneric
\TSQLite3HttpClientGeneric\TSQLRestClientURI
\TSQLRestClientURI\TSQLRestClient
\TSQLRestClient\TSQLRest
\
For a stand-alone application, create a {\f1\fs20 @*TSQLRestClientDB@}. This particular class will initialize an internal {\f1\fs20 @*TSQLRestServerDB@} instance, and you'll have full access to the database in the same process, with no speed penalty.
For a @*Client-Server@ application, create a {\f1\fs20 TSQLRestServerDB} instance, then use the corresponding {\f1\fs20 ExportServer, ExportServerNamedPipe, ExportServerMessage} method to instantiate either a in-process, Named-Pipe or GDI message server. For @*HTTP@/1.1 over TCP/IP, creates a {\f1\fs20 TSQLite3HttpServer} instance, and associate your running {\f1\fs20 TSQLRestServerDB} to it. Then create either a {\f1\fs20 TSQLite3HttpClient}, {\f1\fs20 TSQLRestClientURIDll, TSQLRestClientURINamedPipe} or {\f1\fs20 TSQLRestClientURIMessage} instance to access to your data according to the communication protocol used for the server.
In practice, in order to implement the business logic, you should better create a new class, inheriting from {\f1\fs20 TSQLRestServerDB}. If your purpose is not to have a full {\i SQLite3} engine available, you may create your server from a {\f1\fs20 @*TSQLRestServerFullMemory@} class instead of {\f1\fs20 TSQLRestServerDB}: this will implement a fast in-memory engine (using {\f1\fs20 TSQLRestServerStaticInMemory} instances), with basic CRUD features (for ORM), and persistence on disk as JSON or optimized binary files - this kind of server is enough to handle authentication, and host @*service@s in a stand-alone way. If your services need to have access to a remote ORM server, it may use a {\f1\fs20 @*TSQLRestServerRemoteDB@} class instead: this server will use an internal {\f1\fs20 TSQLRestClient} instance to handle all ORM operations- it can be used e.g. to host some services on a stand-alone server, with all ORM and data access retrieved from another server: it will allow to easily implement a proxy architecture (for instance, as a DMZ for publishing services, but letting ORM process stay out of scope).
All those classes will implement a RESTful access to a remote database, with associated services and business logic.
:  HTTP client
In fact, there are several implementation of a @**HTTP@/1.1 client, according to this class hierarchy:
\graph ClientRESTClasses HTTP/1.1 Client RESTful classes
\TSQLite3HttpClientWinINet\TSQLite3HttpClientWinGeneric
\TSQLite3HttpClientWinHTTP\TSQLite3HttpClientWinGeneric
\TSQLite3HttpClientWinGeneric\TSQLite3HttpClientGeneric
\TSQLite3HttpClientWinSock\TSQLite3HttpClientGeneric
\
So you can select either {\f1\fs20 TSQLite3HttpClientWinSock}, {\f1\fs20 TSQLite3HttpClientWinINet} or {\f1\fs20 TSQLite3HttpClientWinHTTP} for a HTTP/1.1 client.
Each class has its own architecture, and attaches itself to a Windows communication library, all based on {\i WinSock} API. As stated by their name, {\f1\fs20 TSQLite3HttpClientWinSock} will call directly the {\i WinSock} API, {\f1\fs20 TSQLite3HttpClientWinINet} will call {\i WinINet} API (as used by IE 6) and {\f1\fs20 TSQLite3HttpClientWinHTTP} will cal the latest {\i WinHTTP} API:
- {\i WinSock} is the common user-space API to access the sockets stack of Windows, i.e. IP connection - it's able to handle any IP protocol, including TCP/IP, UDP/IP, and any protocol over it (including HTTP);
- {\i WinINet} was designed as an HTTP API client platform that allowed the use of interactive message dialogs such as entering user credentials - it's able to handle HTTP and FTP protocols;
- {\i WinHTTP}'s API set is geared towards a non-interactive environment allowing for use in service-based applications where no user interaction is required or needed, and is also much faster than {\i WinINet} - it only handles HTTP protocol.
\graph ClientHTTPClasses HTTP/1.1 Client architecture
\WinINet\WinSock
\UrlMon\WinINet
\RASAPI\WinINet
................................................................................
|Local speed|Fastest|Slow|Fast
|Network speed|Slow|Medium|Fast
|Minimum OS|Win95/98|Win95/98|Win2000
|HTTPS|Not available|Available|Available
|Integration with IE|None|Excellent (proxy)|Available (see below)
|User interactivity|None|Excellent (authentication, dial-up)|None
|%
As stated above, there is still a potential performance issue to use the direct {\f1\fs20 TSQLite3HttpClientWinSock} class over a network. It has been reported on our forum, and root cause was not identified yet.
Therefore, the {\f1\fs20 TSQLite3HttpClient} class maps by default to the {\f1\fs20 TSQLite3HttpClientWinHTTP} class. This is the recommended usage from a Delphi client application.
Note that even if {\i WinHTTP} does not share by default any proxy settings with Internet Explorer, it can import the current IE settings.  The {\i WinHTTP} proxy configuration is set by either {\f1\fs20 proxycfg.exe} on Windows XP and Windows Server 2003 or earlier, either {\f1\fs20 netsh.exe} on Windows Vista and Windows Server 2008 or later; for instance, you can run "{\f1\fs20 proxycfg -u}" or "{\f1\fs20 netsh winhttp import proxy source=ie}" to use the current user's proxy settings for Internet Explorer. Under @*64 bit@ Vista/Seven, to configure applications using the 32 bit {\i WinHttp} settings, call {\f1\fs20 netsh} or {\f1\fs20 proxycfg} bits from {\f1\fs20 %SystemRoot%\\SysWOW64} folder explicitly.
:  HTTP server using http.sys
:   Presentation
Since {\i Windows XP SP2} and {\i Windows Server 2003}, the Operating System provides a kernel stack to handle @**HTTP@ requests. This {\f1\fs20 http.sys} driver is in fact a full featured HTTP server, running in kernel mode. It is part of the networking subsystem of the {\i Windows} operating system, as a core component.
The {\f1\fs20 SynCrtSock} unit can implement a HTTP server based on this component. Of course, the {\i Synopse mORMot framework} will use it. If it's not available, it will launch our pure Delphi optimized HTTP server, using I/O completion ports and a Thread Pool.
What’s good about https.sys?
- {\i Kernel-mode request queuing}: Requests cause less overhead in context switching, because the kernel forwards requests directly to the correct worker process. If no worker process is available to accept a request, the kernel-mode request queue holds the request until a worker process picks it up.
................................................................................
\ORM HTTP Server\ORM database core\In-process¤no latency
\ORM database core\ORM HTTP Server
\
The BATCH sequence allows you to regroup those statements into just ONE remote call. Internally, it builds a @*JSON@ stream, then post this stream at once to the server. Then the server answers at once, after having performed all the modifications.
Some new {\f1\fs20 TSQLRestClientURI} methods have been added to implement BATCH sequences to speed up database modifications: after a call to {\f1\fs20 BatchStart}, database modification statements are added to the sequence via {\f1\fs20 BatchAdd / BatchUpdate / BatchDelete}, then all statements are sent as one to the remote server via {\f1\fs20 BatchSend} - this is MUCH faster than individual calls to {\f1\fs20 Add / Update / Delete} in case of a slow remote connection (typically @*HTTP@ over Internet).
Since the statements are performed at once, you can't receive the result (e.g. the ID of the added row) on the same time as you append the request to the BATCH sequence. So you'll have to wait for the {\f1\fs20 BatchSend} method to retrieve all results, {\i at once}, in a {\i dynamic} {\f1\fs20 array of integer}.
As you may guess, it's also a good idea to use a @*transaction@ for the whole process. By default, the BATCH sequence is not embedded into a transaction. It's up to the caller to use a {\f1\fs20 TransactionBegin} ... {\f1\fs20 try}... {\f1\fs20 Commit  except RollBack} block.
Here is a typical use (extracted from the regression @*test@s in {\f1\fs20 SQLite3.pas}:
!// start the transaction
!!if ClientDist.TransactionBegin(TSQLRecordPeople) then
!try
!  // start the BATCH sequence
!!  Check(ClientDist.BatchStart(TSQLRecordPeople));
!  // delete some elements
!  for i := 0 to n-1 do
................................................................................
!end;
The only not obvious part of this code is the parameters marshaling, i.e. how the values are retrieved from the incoming {\f1\fs20 aParams.Parameters} text buffer, then converted into native local variables.
On the Server side, typical implementation steps are therefore:
- Use the {\f1\fs20 UrlDecodeNeedParameters} function to check that all expected parameters were supplied by the caller in {\f1\fs20 aParams.Parameters};
- Call {\f1\fs20 UrlDecodeInteger / UrlDecodeInt64 / UrlDecodeExtended / UrlDecodeValue / UrlDecodeObject} functions (all defined in {\f1\fs20 SynCommons.pas}) to retrieve each individual parameter from standard JSON content;
- Implement the service (here it is just the {\f1\fs20 a+b} expression);
- Then return the result into {\f1\fs20 aParams.Resp} variable.
The powerful {\f1\fs20 UrlDecodeObject} function (defined in {\f1\fs20 SQLite3Commons.pas}) can be used to un-serialize most class instance from its textual JSON representation ({\f1\fs20 @*TPersistent@, @*TSQLRecord@, TStringList}...).
Note that due to this implementation pattern, the {\i mORMot} service implementation is very fast, and not sensitive to the "Hash collision attack" security issue, as reported with {\i Apache} - see @http://blog.synopse.info/post/2011/12/30/Hash-collision-attack for details.
The implementation must return the HTTP error code (e.g. 200 on success) as an integer value, and any response in {\f1\fs20 aParams.Resp} as a serialized JSON object by default (using e.g. {\f1\fs20 TSQLRestServer.JSONEncodeResult}), since default mime-type is {\f1\fs20 JSON_CONTENT_TYPE}:
$ {"Result":"OneValue"}
or a JSON object containing an array:
$ {"Result":["One","two"]}
So you can consume these services, implemented Server-Side in fast Delphi code, with any @*AJAX@ application on the client side (if you use HTTP as communication protocol).
The {\f1\fs20 aParams.Head^} parameter may be overridden on the server side to set a custom header which will be provided to the client - it may be useful for instance to specify another mime-type than the default constant {\f1\fs20 JSON_CONTENT_TYPE}, i.e. {\f1\fs20 'application/json; charset=UTF-8'}, and returns plain text, HTML or binary.
................................................................................
!function Sum(aClient: TSQLRestClientURI; a, b: double): double;
!var err: integer;
!begin
!  val(aClient.CallBackGetResult('sum',['a',a,'b',b]),Result,err);
!end;
You could even implement this method in a dedicated client method - which make sense:
!type
!  TMyClient = class(TSQLite3HttpClient) // could be TSQLRestClientURINamedPipe
!  (...)
!    function Sum(a, b: double): double;
!  (...)
!
!function TMyClient.Sum(a, b: double): double;
!var err: integer;
!begin
................................................................................
The server is implemented as such:
!program Project14Server;
!
!{$APPTYPE CONSOLE}
!
!uses
!  SysUtils,
!  SQLite3Commons,
!  SQLite3,
!  Project14Interface;
!
!type
!  TServiceCalculator = class(TInterfacedObject, ICalculator)
!  public
!    function Add(n1,n2: integer): integer;
!  end;
................................................................................
- A {\f1\fs20 TSQLRestClientURINamedPipe} instance is created, with an associate {\f1\fs20 TSQLModel} and the given {\f1\fs20 APPLICATION_NAME} to access the proper server via a named pipe communication;
- The connection is authenticated with the default {\f1\fs20 'User'} rights;
- The {\f1\fs20 ICalculator} interface is defined in the client's internal factory, in {\f1\fs20 sicShared} mode (just as in the server).
Once the client is up and ready, the local {\f1\fs20 I: ICalculator} variable instance is retrieved, and the remote service is called directly via a simple {\f1\fs20 I.Add(a,b)} statement.
You can imagine how easy and safe it will be to implement a @17@ for your future applications, using {\i mORMot}.
:   Implementation details
:    Involved classes
You will find out in {\f1\fs20 SQLite3Commons.pas} all classes implementing this interface communication.
\graph HierTServiceContainerServer Services implementation classes hierarchy
\TServiceFactoryServer\TServiceFactory
\TServiceFactoryClient\TServiceFactory
\TServiceContainerClient\TServiceContainer
\TServiceContainerServer\TServiceContainer
\
There are two levels of implementation:
................................................................................
|Password on Server|Yes|Yes/No|N/A
|Truly Stateless|Yes|No|Yes
|Truly RESTful|No|No|Yes
|HTTP-free|No|No|Yes
|%
:   HTTP basic auth over HTTPS
This first solution, based on the standard @**HTTPS@ protocol, is used by most web services. It's easy to implement, available by default on all browsers, but has some known draw-backs, like the awful authentication window displayed on the Browser, which will persist (there is no {\i LogOut}-like feature here), some server-side additional CPU consumption, and the fact that the user-name and password are transmitted (over HTTPS) into the Server (it should be more secure to let the password stay only on the client side, during keyboard entry, and be stored as secure hash on the Server).
The supplied {\f1\fs20 TSQLite3HttpClientWinHTTP} and {\f1\fs20 TSQLite3HttpClientWinINet} clients classes are able to connect using HTTPS, and the {\f1\fs20 THttpApiServer} server class can send compatible content.
:   Session via Cookies
To be honest, a @**session@ managed on the Server is not truly @*Stateless@. One possibility could be to maintain all data within the cookie content. And, by design, the cookie is handled on the Server side (Client in fact don’t even try to interpret this cookie data: it just hands it back to the server on each successive request). But this cookie data is application state data, so the client should manage it, not the server, in a pure Stateless world.
The cookie technique itself is HTTP-linked, so it's not truly RESTful, which should be protocol-independent. Since our framework does not provide only HTTP protocol, but offers other ways of transmission, Cookies were left at the baker's home.
:   Query Authentication
{\i @**Query Authentication@} consists in signing each RESTful request via some additional parameters on the URI. See @http://broadcast.oreilly.com/2009/12/principles-for-standardized-rest-authentication.html about this technique. It was defined as such in this article:
{\i All REST queries must be authenticated by signing the query parameters sorted in lower-case, alphabetical order using the private credential as the signing token. Signing should occur before URI encoding the query string.}
For instance, here is a generic URI sample from the link above:
................................................................................
Some working @**JavaScript@ code has been published in our forum by a framework user (thanks, "RangerX"), which implements the authentication schema as detailed above. It uses {\f1\fs20 jQuery}, and HTML 5 {\f1\fs20 LocalStorage}, not cookies, for storing session information on the Client side.
See @http://synopse.info/forum/viewtopic.php?pid=2995#p2995
The current revision of the framework contains the code as expected by this JavaScript code - especially the results encoded as @2@ objects.
In the future, some "official" code will be available for such AJAX clients. It will probably rely on pure-pascal implementation using such an {\i Object-Pascal-to-JavaScript} compiler - it does definitively make sense to have Delphi-like code on the client side, not to break the @*ORM@ design. For instance, the Open Source {\f1\fs20 DWS} ({\i DelphiWebScript}) compiler matches our needs - see @http://delphitools.info/tag/javascript
:12 Testing
:25  Thread-safety
On the Server side, our Framework was designed to be @**thread-safe@.
In fact, the {\f1\fs20 TSQLRestServer.URI} method is expected to be thread-safe, e.g. from the {\f1\fs20 TSQLite3HttpServer. Request} method. Thanks to the @*REST@ful approach of our framework, this method is the only one which is expected to be thread-safe.
In order to achieve this thread-safety without sacrificing performance, the following rules were applied in {\f1\fs20 TSQLRestServer.URI}:
- Most of this methods's logic is to process the incoming parameters, so is thread-safe by design (e.g. {\f1\fs20 Model} and {\f1\fs20 RecordProps} access do not change during process);
- The {\i @*SQLite3@} engine access is protected at SQL/JSON @*cache@ level, via {\f1\fs20 DB.LockJSON()} calls in {\f1\fs20 @*TSQLRestServerDB@} methods;
- {\f1\fs20 TSQLRestServerStatic} main methods ({\f1\fs20 EngineList, EngineRetrieve, EngineAdd, EngineUpdate, EngineDelete, EngineRetrieveBlob, EngineUpdateBlob}) are thread-safe: e.g. {\f1\fs20 @*TSQLRestServerStaticInMemory@} uses a per-Table Critical Section;
- {\f1\fs20 TSQLRestServerCallBack} methods (i.e. @*published method@s of the inherited {\f1\fs20 TSQLRestServer} class) must be implemented to be thread-safe;
- A protected {\f1\fs20 fSessionCriticalSection} is used to protect shared {\f1\fs20 fSession[]} access between clients;
- Remote external tables - see @27@ - use thread-safe connections and statements when accessing the databases via SQL;
................................................................................
- Any exceptions triggered during process via {\f1\fs20 sllException} and {\f1\fs20 sllExceptionOS} levels;
- Client and server @*REST@ful {\f1\fs20 URL} methods via {\f1\fs20 sllClient} and {\f1\fs20 sllServer} levels;
- @*SQL@ executed statements in the {\i @*SQLite3@} engine via the {\f1\fs20 sllSQL} level;
- @*JSON@ results when retrieved from the {\i SQLite3} engine via the {\f1\fs20 sllResult} level;
- Main errors triggered during process via {\f1\fs20 sllError} level;
- @*Security@ User authentication and @*session@ management via {\f1\fs20 sllUserAuth};
- Some additional low-level information via {\f1\fs20 sllDebug} and {\f1\fs20 sllInfo} levels.
Those levels are available via the {\f1\fs20 TSQLLog} class, inheriting from {\f1\fs20 TSynLog}, as defined in @!TSQLLog!Lib\SQLite3\SQLite3Commons.pas@.
Three main {\f1\fs20 TSynLogClass} global variables are defined in order to use the same {\f1\fs20 TSynLog} class for all logging available in the framework units. Since all layers are not common, several variables have been defined, as such:
- {\f1\fs20 SynDBLog} for all {\i @*SynDB@*} units, i.e. all generic database code;
- {\f1\fs20 SQLite3Log} for all {\i SQLite3*} units, i.e. all @*ORM@ related code;
- {\f1\fs20 SynSQLite3Log} for the {\f1\fs20 SynSQLite3} unit, which implements the {\i @*SQLite3@} engine itself.
For instance, if you execute the following statement at the beginning of {\f1\fs20 TestSQL3.dpr}, most regression @*test@s will produce some logging, and will create about 270 MB of log file content, if executed:
!  with TSQLLog.Family do begin
!    Level := LOG_VERBOSE;
!    HighResolutionTimeStamp := true;
!    TSynLogTestLog := TSQLLog;
!    SynDBLog := TSQLLog;
................................................................................
As retrieved from our source code repository, you'll find the following file layout.
|%30%70
|\b Directory|Description\b0
|{\f1\fs20 /}|Root folder, containing common files
|{\f1\fs20 HtmlView/}|A fork of the freeware {\f1\fs20 THtmlView} component, used as a demo of the {\f1\fs20 SynPdf} unit - not finished, and not truly Unicode ready
|{\f1\fs20 LVCL/}|{\i Light VCL} replacement files for standard VCL (for Delphi 6-7 only)
|{\f1\fs20 RTL7/}|Enhanced RTL .dcu for Delphi 7 (not mandatory at all), and {\i FastMM4} memory manager to be used before Delphi 2006
|{\f1\fs20 SQLite3/}|Contains all @*ORM@ related files of the framework
|{\f1\fs20 SynProject/}|Source code of the {\i SynProject} tool, used to create e.g. this documentation
;|{\f1\fs20 zeos/}|A fork of the freeware {\i Zeos} library - not finished, and not truly Unicode ready
|%
In the {\i Root folder}, some common files are defined:
|%30%70
|\b File|Description\b0
|{\f1\fs20 CPort.*}|A fork of the freeware {\i ComPort} Library ver. 2.63
................................................................................
|\b File|Description\b0
|{\f1\fs20 @*SynDB@}|abstract database direct access classes
|{\f1\fs20 SynOleDB}|fast @*OleDB@ direct access classes
|{\f1\fs20 SynDBODBC}|fast @*ODBC@ direct access classes
|{\f1\fs20 SynDBOracle}|{\i @*Oracle@} DB direct access classes (via OCI)
|{\f1\fs20 SynDBSQLite3}|{\i @*SQLite3@} direct access classes
|%
In the {\f1\fs20 SQlite3/} folder, the files defining the {\i Synopse ORM framework} (using mostly {\f1\fs20 SynCommons, SynLZ, SynSQLite3, SynGdiPlus, SynCrtSock, SynPdf, SynTaskDialog} and {\f1\fs20 SynZip} from the {\i Root folder}):
|%30%70
|\b File|Description\b0
|{\f1\fs20 Documentation/}|Sub folder containing the source of the Synopse documentation
|{\f1\fs20 Samples/}|Sub folders containing some sample code
|{\f1\fs20 SQLite3Commons.pas}|Main unit of the @*ORM@ framework
|{\f1\fs20 SQLite3.pas}|{\i SQLite3} kernel bridge between {\f1\fs20 SQLite3Commons.pas} and {\f1\fs20 SynSQLite3.pas}
|{\f1\fs20 *.bmp *.rc}|Resource files, compiled into {\f1\fs20 *.res} files
|{\f1\fs20 SQLite3FastCgiServer.pas}|FastCGI server - not fully tested
|{\f1\fs20 SQLite3HttpClient.pas}|HTTP/1.1 Client
|{\f1\fs20 SQLite3HttpServer.pas}|HTTP/1.1 Server
|{\f1\fs20 SQLite3Pages.pas}|Integrated Reporting engine
|{\f1\fs20 SQLite3Service.pas}|Stand-alone Service
|{\f1\fs20 SQLite3ToolBar.pas}|ORM ToolBar User Interface generation
|{\f1\fs20 SQLite3UI.*}|Grid to display Database content
|{\f1\fs20 SQLite3UIEdit.*}|Record edition dialog, used to edit record content on the screen
|{\f1\fs20 SQLite3UILogin.*}|some common User Interface functions and dialogs
|{\f1\fs20 SQLite3UIOptions.*}|General Options setting dialog, generated from code
|{\f1\fs20 SQLite3UIQuery.*}|Form handling queries to a User Interface Grid, using our ORM RTTI to define search parameters and algorithms
|{\f1\fs20 SQLite3i18n.pas}|internationalization (@*i18n@) routines and classes
|{\f1\fs20 TestSQL3.dpr}|Main unit @*test@ing program of the Synopse {\i mORMot} framework
|{\f1\fs20 TestSQL3Register.dpr}|Run as administrator for {\i TestSQL3} to use {\i http.sys} on Vista/Seven
|{\f1\fs20 c.bat sqlite3.c}|Source code of the {\i SQLite3} embedded Database engine
|%
: Installation
Just unzip the {\f1\fs20 .zip} archive, including all sub-folders, into a local directory of your computer (for instance, {\f1\fs20 D:\Dev\Lib}).
Then add the following paths to your Delphi IDE (in {\i Tools/Environment/Library} menu):
................................................................................
- Enhanced @*log@ging.
:32 Unicode and UTF-8
Our {\i mORMot} Framework has 100% UNICODE compatibility, that is compilation under Delphi 2009/2010/XE/XE2/XE3. The code has been deeply rewritten and @*test@ed, in order to provide compatibility with the {\f1\fs20 String=UnicodeString} paradigm of these compilers.  But the code will also handle safely Unicode for older version, i.e. from Delphi 6 up to Delphi 2007.
Since our framework is natively @**UTF-8@ (this is the better character encoding for fast text - @*JSON@ - streaming/parsing and it is natively supported by the {\i SQLite3} engine), we had to establish a secure way our framework used strings, in order to handle all versions of Delphi (even pre-Unicode versions, especially the Delphi 7 version we like so much), and provide compatibility with the Free Pascal Compiler.
Some string types have been defined, and used in the code for best cross-compiler efficiency (avoiding most conversion between formats):
- {\f1\fs20 @*RawUTF8@} is used for every internal data usage, since both {\i @*SQLite3@} and JSON do expect UTF-8 encoding;
- {\f1\fs20 WinAnsiString} where {\i WinAnsi}-encoded {\f1\fs20 AnsiString} (code page 1252) are needed;
- Generic {\f1\fs20 string} for {\i @*i18n@} (e.g. in unit {\f1\fs20 SQLite3i18n}), i.e. text ready to be used within the VCL, as either {\f1\fs20 AnsiString} (for Delphi 2 to 2007) or {\f1\fs20 UnicodeString} (for Delphi 2009 and later);
- {\f1\fs20 RawUnicode} in some technical places (e.g. direct Win32 *W() API call in Delphi 7) - note: this type is NOT compatible with Delphi 2009 and later {\f1\fs20 UnicodeString};
- {\f1\fs20 RawByteString} for byte storage (e.g. for {\f1\fs20 FileFromString()} function);
- {\f1\fs20 SynUnicode} is the fastest available Unicode {\i native} string type, depending on the compiler used (i.e. {\f1\fs20 WideString} before Delphi 2009, and {\f1\fs20 UnicodeString} since);
- Some special conversion functions to be used for Delphi 2009+ {\f1 UnicodeString} (defined inside {\f1\fs20 \{$ifdef UNICODE\}...\{$endif\}} blocks);
- Never use {\f1\fs20 AnsiString} directly, but one of the types above.
Note that {\f1\fs20 RawUTF8} is the preferred {\f1\fs20 string} type to be used in our framework when defining textual properties in a {\f1\fs20 @*TSQLRecord@} and for all internal data processing. It's only when you're reaching the User Interface layer that you may convert explicitly the {\f1\fs20 RawUTF8} content into the generic VCL {\f1\fs20 string} type, using either the {\f1\fs20 Language. UTF8ToString} method (from {\f1\fs20 SQLite3i18n.pas} unit) or the following function from {\f1\fs20 SynCommons.pas}:
!/// convert any UTF-8 encoded String into a generic VCL Text
!// - it's prefered to use TLanguageFile.UTF8ToString() in SQLite3i18n,
!// which will handle full i18n of your application
!// - it will work as is with Delphi 2009+ (direct unicode conversion)
!// - under older version of Delphi (no unicode), it will use the
!// current RTL codepage, as with WideString conversion (but without slow
!// WideString usage)
!function UTF8ToString(const Text: RawUTF8): string;
Of course, the {\f1\fs20 StringToUTF8} method or function are available to send back some text to the @*ORM@ layer.\line A lot of dedicated conversion functions (including to/from numerical values) are included in {\f1\fs20 SynCommons.pas}. Those were optimized for speed and multi-thread capabilities, and to avoid implicit conversions involving a temporary {\f1\fs20 string} variable.
................................................................................
This code will therefore:
- Save the current register context via {\f1\fs20 pushad / popad} opcodes pair;
- Check if {\f1\fs20 TSynLog} should intercept exceptions (i.e. if the global {\f1\fs20 SynLogExceptionEnabled} boolean is true);
- Call our logging function {\f1\fs20 LogExcept};
- Call the default Windows {\f1\fs20 RtlUnwind} API, as expected by the Operating System.
:  Serialization
{\i @*dynamic array@s} can also be serialized as @*JSON@ in the log on request, via the default {\f1\fs20 TSynLog} class, as defined in {\f1\fs20 SynCommons} unit - see @48@.
The {\f1\fs20 TSQLLog} class (using the enhanced @*RTTI@ methods defined in {\f1\fs20 SQLite3Commons} unit) is even able to serialize {\f1\fs20 @*TSQLRecord@, @*TPersistent@, TList} and {\f1\fs20 @*TCollection@} instances as JSON, or any other class instance, after call to {\f1\fs20 TJSONSerializer. @*RegisterCustomSerializer@}.
For instance, the following code:
!procedure TestPeopleProc;
!var People: TSQLRecordPeople;
!    Log: ISynLog;
!begin
!  Log := TSQLLog.Enter;
!  People := TSQLRecordPeople.Create;
................................................................................
You could of course design your own User Interface without our framework. That is, this is perfectly feasible to use only the @*ORM@ part of it. For instance, it should be needed to develop @*AJAX@ applications using its @*REST@ful model - see @9@ - since such a feature is not yet integrated to our provided source code.
But for producing easily applications, the framework provides a mechanism based on both ORM description and @*RTTI@ compiler-generated information in order to create most User Interface by code.
It is able to generated a Ribbon-based application, in which each table is available via a Ribbon tab, and some actions performed to it.
So the framework would need to know:
- Which tables must be displayed;
- Which actions should be associated with each table;
- How the User Interface should be customized (e.g. hint texts, grid layout on screen, reporting etc...);
- How generic automated edition, using the @!TRecordEditForm!Lib\SQLite3\SQLite3UIEdit.pas@ unit, is to be generated.
To this list could be added an integrated event feature, which can be linked to actions and custom status, to provide a centralized handling of user-level @*log@ing (as used e.g. in the {\i SynFile} {\f1\fs20 TSQLAuditTrail} table) - please do not make confusion between this user-level logging and technical-level logging using {\f1\fs20 TSynLog} and {\f1\fs20 TSQLLog} classes and "families" - see @16@.
: Rendering
The current implementation of the framework User Interface generation handles two kind of rendering:
- Native VCL components;
- Proprietary TMS components.
You can select which set of components are used, by defining - globally to your project (i.e. in the {\i Project/Options/Conditionals} menu) - the {\f1\fs20 USETMSPACK} conditional. If it is not set (which is by default), it will use VCL components.
The native VCL components will use native Windows API components. So the look and feel of the application will vary depending on the Windows version it is running on. For instance, the resulting screen will be diverse if the application is run under Windows 2000, XP, Vista and Seven. The "ribbon" as generated with VCL components has most functionalities than the Office 2007/2010 ribbon, but will have a very diverse layout.
................................................................................
|Ribbon look|Unusual|Office-like
|Preview button & Shortcuts|None by default|Available
|Extra Price|None|High
|GPL ready|Yes|No
|Office UI Licensing|N/A|Required
|EXE size|Smaller|Bigger
|%
It's worth saying that the choice of one or other component set could be changed on request. If you use the generic components as defined in {\f1\fs20 SQLite3ToolBar} (i.e. the {\f1\fs20 TSynForm, TSynToolBar, TSynToolButton, TSynPopupMenu, TSynPage, TSynPager, TSynBodyPager} and {\f1\fs20 TSynBodyPage} classes) and {\f1\fs20 SynTaskDialog} (for {\f1\fs20 TSynButton}) in your own code, the {\f1\fs20 USETMSPACK} conditional will do all the magic for you.
The {\i Office UI licensing program} was designed by {\i Microsoft} for software developers who wish to implement the Office UI as a software component and/or incorporate the Office UI into their own applications. If you use TMS ribbon, it will require acceptance of the Office UI License terms as defined at @http://msdn.microsoft.com/en-us/office/aa973809.aspx
If you want to design your user interface using a Office 2007/2010 ribbon look, please take a look at those official guidelines: @http://msdn.microsoft.com/en-us/library/cc872782.aspx
Here is the screen content, using the TMS components:
%synfiletms.png
And here is the same application compiled using only VCL components, available from Delphi 6 up to XE3:
%synfilevcl.png
We did not use yet the Ribbon component as was introduced in Delphi 2009. Its action-driven design won't make it easy to interface with the event-driven design of our User Interface handling, and we have to confess that this component has rather bad reputation (at least in the Delphi 2009 version). Feel free to adapt our Open Source code to use it - we'll be very pleased to release a new version supporting it, but we don't have time nor necessity to do it by ourself.
: Enumeration types
A list of available actions should be defined, as an enumeration type:
!  TFileAction = (
!    faNoAction, faMark, faUnmarkAll, faQuery, faRefresh, faCreate,
!    faEdit, faCopy, faExport, faImport, faDelete, faSign, faPrintPreview,
!    faExtract, faSettings );
Thanks to the Delphi @*RTTI@, and "{\i Un @*Camel@ Casing}", the following list will generate a set of available buttons on the User Interface, named "Mark", "Unmark all", "Query", "Refresh", "Create", "Edit", "Copy", "Export", "Import", "Delete", "Sign", "Print preview", "Extract" and "Settings". Thanks to the @!TLanguageFile.Translate!Lib\SQLite3\SQLite3i18n.pas@ unit (responsible of application @*i18n@) and the {\f1\fs20 TLanguageFile. Translate} method, it could be translated on-the-fly from English into the current desired language, before display on screen or report creation.
See both above screen-shots to guess how the button captions match the enumeration names - i.e. @%synfilevcl.png@ and @%synfilevcl.png@.
A list of events, as used for the {\f1\fs20 TSQLAuditTrail} table, was also defined. Some events reflect the change made to the database rows (like {\f1\fs20 feRecordModified}), or generic application status (like {\f1\fs20 feServerStarted}):
!  TFileEvent = (
!    feUnknownState, feServerStarted, feServerShutdown,
!    feRecordCreated, feRecordModified, feRecordDeleted,
!    feRecordDigitallySigned, feRecordImported, feRecordExported );
In the grid and the reports, @*RTTI@ and "{\i uncamelcasing}" will be used to display this list as regular text, like "{\i Record digitally signed}", and translated to the current language, if necessary.
................................................................................
!  /// class used to create the User interface
!  TFileRibbon = class(TSQLRibbon)
!  public
!    /// overriden method used customize the report content
!    procedure CreateReport(aTable: TSQLRecordClass; aID: integer; aReport: TGDIPages;
!      AlreadyBegan: boolean=false); override;
!  end;
The reporting engine in the framework is implemented via the {\f1\fs20 TGDIPages} class, defined in the @!TGDIPages!Lib\SQLite3\SQLite3Pages.pas@:
- Data is drawn in memory, they displayed or printed as desired;
- High-level reporting methods are available (implementing tables, columns, titles and such), but you can have access to a {\f1\fs20 TCanvas} property which allows any possible content generation via standard VCL methods;
- Allow preview (with anti-aliased drawing via GDI+) and printing;
- Direct export as {\f1\fs20 .txt} or {\f1\fs20 .@*pdf@} file;
- Handle bookmark, outlines and links inside the document.
By default, the {\f1\fs20 CreateReport} method of {\f1\fs20 TSQLRibbon} will write all editable fields value to the content.
The method is overridden by the following code:
................................................................................
!    end;
Report header is written using the following methods:
- {\f1\fs20 DrawTitle} to add a title to the report, with a black line below it (second parameter to {\f1\fs20 true}) - this title will be added to the report global outline, and will be exported as such in {\f1\fs20 .pdf} on request;
- {\f1\fs20 NewHalfLine} and {\f1\fs20 NewLine} will leave some vertical gap between two paragraphs;
- {\f1\fs20 AddColumns}, with parameters set as percentages, will initialize a table with the first column content defined as bold ({\f1\fs20 SetColumnBold(0)});
- {\f1\fs20 DrawTextAcrossCols} and {\f1\fs20 DrawTextAcrossColsFromCSV} will fill a table row according to the text specified, one string per column;
- {\f1\fs20 DrawBMP} will draw a bitmap to the report, which content is loaded using the generic {\f1\fs20 LoadFromRawByteString} function implemented in @!Lib\SynGdiPlus.pas@;
- {\f1\fs20 U2S} and {\f1\fs20 Iso2S} function, as defined in @!Iso2S,U2S!Lib\SQLite3\SQLite3i18n.pas@, are used for conversion of some text or {\f1\fs20 @*TTimeLog@} into a text formated with the current language settings (@*i18n@).
!    // write report content
!    DrawTitle(sContent,true);
!    SaveLayout;
!    Font.Name := 'Courier New';
!    if Rec.InheritsFrom(TSQLSafeMemo) then
!      DrawText(sSafeMemoContent) else
!    if Rec.InheritsFrom(TSQLMemo) then
................................................................................
!  sSizeN = 'Size: %s';
!  sContentTypeN = 'Content Type: %s';
!  sSafeMemoContent = 'This memo is password protected.'#13+
!    'Please click on the "Edit" button to show its content.';
!  sDataContent = 'Please click on the "Extract" button to get its content.';
!  sSignedN = 'Signed,By %s on %s';
!  sPictureN = '%s Picture';
The @!Lib\SQLite3\SQLite3i18n.pas@ unit is able to parse all those {\f1\fs20 resourcestring} from a running executable, via its {\f1\fs20 ExtractAllResources} function, and create a reference text file to be translated into any handled language.
Creating a report from code does make sense in an ORM. Since we have most useful data at hand as Delphi classes, code can be shared among all kind of reports, and a few lines of code is able to produce complex reports, with enhanced rendering, unified layout, direct internationalization and export capabilities.
: Application i18n and L10n
In computing, internationalization and localization (also spelled internationalisation and localisation) are means of adapting computer software to different languages, regional differences and technical requirements of a target market:
- {\i Internationalization} (@**i18n@) is the process of designing a software application so that it can be adapted to various languages;
- {\i Localization} (@**L10n@) is the process of adapting internationalized software for a specific region or language by adding locale-specific components and translating text, e.g. for dates display.
Our framework handles both features, via the @!Lib\SQLite3\SQLite3i18n.pas@ unit. We just saw above how {\f1\fs20 @*resourcestring@} defined in the source code are retrieved from the executable and can be translated on the fly. The unit extends this to visual forms, and even captions generated from @*RTTI@ - see @5@.
The unit expects all textual content (both {\f1\fs20 resourcestring} and RTTI derived captions) to be correct English text. A list of all used textual elements will be retrieved then hashed into an unique numerical value. When a specific locale is set for the application, the unit will search for a {\f1\fs20 @*.msg@} text file in the executable folder matching the expected locale definition. For instance, it will search for {\f1\fs20 FR.msg} for translation into French.
In order to translate all the user interface, a corresponding {\f1\fs20 .msg} file is to be supplied in the executable folder. Neither the source code, nor the executable is to be rebuild to add a new language. And since this file is indeed a plain textual file, even a non developer (e.g. an end-user) is able to add a new language, starting from another {\f1\fs20 .msg}.
:  Creating the reference file
In order to begin a translation task, the {\f1\fs20 SQlite3i18n.pas} unit is able to extract all textual resource from the executable, and create a reference text file, containing all English sentences and words to be translated, associated with their numerical hash value.
It will in fact:
- Extract all {\f1\fs20 resourcestring} text;
- Extract all captions generated from RTTI (e.g. from enumerations or class properties names);
- Extract all embedded {\f1\fs20 dfm} resources, and create per-form sections, allowing a custom translation of displayed captions or hints.
This creation step needs a compilation of the executable with the {\f1\fs20 EXTRACTALLRESOURCES} conditional defined, {\i globally} to the whole application (a full {\i rebuild} is necessary after having added or suppressed this conditional from the {\i Project / Options / Folders-Conditionals} IDE field).
Then the {\f1\fs20 ExtractAllResources} global procedure is to be called somewhere in the code.
For instance, here is how this is implemented in @!TMainForm.FormShow!Lib\SQLite3\Samples\MainDemo\FileMain.pas@, for the framework main demo:
................................................................................
$KeyWords.EditLabel.Caption=_3731019706   KeyWords
$
$[TLoginForm]
$Label1.Caption=_1741937413   &User name:
$Label2.Caption=_4235002365   &Password:
$
$[TMainForm]
$Caption=_16479868    Synopse SQLite3 Framework demo - SynFile
$
$[Messages]
$2784453965=Memo
$2751226180=Data
$744738530=Safe memo
$895337940=Safe data
$2817614158=Name
$1741937413=&User name:
$4235002365=&Password:
$16479868= Synopse SQLite3 Framework demo - SynFile
$940170664=Content
$3153227598=None
$3708724895=Page %d / %d
$2767358349=Size: %s
$4281038646=Content Type: %s
$2584741026=This memo is password protected.|Please click on the "Edit" button to show its content.
$3011148197=Please click on the "Extract" button to get its content.
................................................................................
!!  i18nLanguageToRegistry(lngFrench);
!  Ribbon.ToolBar.ActivePageIndex := 1;
!end;
Above code will set the main application language as French. At next startup, the content of a supplied {\f1\fs20 SynFileFR.msg} file will be used to translate all screen layout, including all RTTI-generated captions.
Of course, for a final application, you'll need to change the language by a common setting. See {\f1\fs20 i18nAddLanguageItems, i18nAddLanguageMenu} and {\f1\fs20 i18nAddLanguageCombo} functions and procedures to create your own language selection dialog, using a menu or a combo box, for instance.
:  Localization
Take a look at the {\f1\fs20 TLanguageFile} class. After the main language has been set, you can use the global {\f1\fs20 Language} instance in order to localize your application layout.
The {\f1\fs20 SQlite3i18n} unit will register itself to some methods of {\f1\fs20 SQlite3Commons.pas}, in order to translate the RTTI-level text into the current selected language. See for instance {\f1\fs20 i18nDateText}.

[SAD-Ajax]
SourcePath=
IncludePath=
SourceFile=
Version=1.18
DisplayName=Ajax clients for mORMot
................................................................................
- Synopse work on the framework is distributed without any warranty, according to the chosen license terms;
- This documentation is released under the GPL (GNU General Public License) terms, without any warranty of any kind.
=[GPL]

[SDD-DI-2.1.1]
; SRS-DI-2.1.1 - The framework must be Client-Server oriented
:Implementation
The @*Client-Server@ aspect of the framework is implemented in the @!TSQLRecord,TSQLRest,TSQLRestServer,TSQLRestClientURI,TSQLTableJSON!Lib\SQLite3\SQLite3Commons.pas@ unit, with the {\f1\fs20 TSQLRestServer} and {\f1\fs20 TSQLRestClientURI} classes.
Both classes inherit from a generic {\f1\fs20 TSQLRest} class, which implements some generic database access methods and properties (through @*ORM@ model for objects descending from {\f1\fs20 TSQLRecord} or table-based query using {\f1\fs20 TSQLTableJSON}).

[SDD-DI-2.1.1.1]
; SRS-DI-2.1.1.1 - A RESTful mechanism must be implemented
:Implementation
The @*REST@ful mechanism is implemented using the {\f1\fs20 URI} method of both {\f1\fs20 TSQLRestServer} and {\f1\fs20 TSQLRestClientURI} classes, as defined in the @!TSQLRest,TSQLRestServer,TSQLRestClientURI!Lib\SQLite3\SQLite3Commons.pas@ unit.
: Server-Side
In the {\f1\fs20 TSQLRestServer} class, the {\f1\fs20 URI} method is defined as {\f1\fs20 public}, and must implement the actual database query or update, according to the REST request:
!function TSQLRestServer.URI(const url, method: RawUTF8; const SentData: RawUTF8;
!      out Resp, Head: RawUTF8; const RestAccessRights: TSQLAccessRights): Int64Rec;
The purpose of this method is to:
- Return internal database state count (used for caching);
- Retrieve URI expecting the RESTful {\f1\fs20 'ModelRoot[/TableName[/ID[/BlobFieldName]]]'} format;
................................................................................

[SDD-DI-2.1.1.2]
; SRS-DI-2.1.1.2 - Commmunication should be available directly in the same process memory, or remotly using Named Pipes, Windows messages or HTTP/1.1 protocols

[SDD-DI-2.1.1.2.1]
; SRS-DI-2.1.1.2.1 - Client-Server Direct communication inside the same process
:Implementation
The in-process communication is implemented by using a global function, named {\f1\fs20 URIRequest} and defined in @!TSQLRestServer,URIRequest,USEFASTMM4ALLOC,TSQLRestClientURIDll.Create!Lib\SQLite3\SQLite3Commons.pas@:
!function URIRequest(url, method, SendData: PUTF8Char; Resp, Head: PPUTF8Char): Int64Rec; cdecl;
: Server-Side
This function can be exported from a DLL to remotely access to a {\f1\fs20 TSQLRestServer}, or used in the same process:
- Use {\f1\fs20 TSQLRestServer.ExportServer} to assign a server to this function;
- Return {\i 501 NOT IMPLEMENTED} error if no {\f1\fs20 TSQLRestServer.ExportServer} has been assigned yet;
- Memory for {\f1\fs20 Resp} and {\f1\fs20 Head} parameters are allocated with {\f1\fs20 GlobalAlloc()} Win32 API function: client must release this pointers with {\f1\fs20 GlobalFree()} after having retrieved their content - you can force using the Delphi heap (and {\f1\fs20 GetMem} function which is much faster than {\f1\fs20 GlobalAlloc}) by setting the {\f1\fs20 USEFASTMM4ALLOC} variable to TRUE: in this case, client must release this pointers with {\f1\fs20 Freemem()}.
: Client-Side
The Client should simply use a {\f1\fs20 TSQLRestClientURIDll} instance to access to an exported {\f1\fs20 URIRequest()} function.

[SDD-DI-2.1.1.2.2]
; SRS-DI-2.1.1.2.2 - Client-Server Named Pipe communication
:Implementation
: Server-Side
The communication is implemented by using the {\f1\fs20 TSQLRestServer} class, defined in @!TSQLRestServer.ExportServerNamedPipe,TSQLRestClientURINamedPipe.Create,TSQLRestClientURI!Lib\SQLite3\SQLite3Commons.pas@.
This class implements a server over Named Pipe communication, when its {\f1\fs20 ExportServerNamedPipe} method is called.
: Client-Side
A dedicated {\f1\fs20 TSQLRestClientURINamedPipe} class has been defined. It inherits from {\f1\fs20 TSQLRestClientURI}, and override its {\f1\fs20 URI} protected method so that it communicates using a specified Named Pipe.

[SDD-DI-2.1.1.2.3]
; SRS-DI-2.1.1.2.3 - Client-Server Windows Messages communication
:Implementation
Communication using Win32 GDI messages is very handy and efficient on the same computer. It's also perfectly safe, because, by design, it can't be access remotely. Performances for small messages is also excellent. Named pipe could be faster only when bigger messages are transmitted.
: Server-Side
The communication is implemented by using the {\f1\fs20 TSQLRestServer} class, defined in @!TSQLRestClientURI,TSQLRestServer.ExportServerMessage,TSQLRestClientURIMessage.Create!Lib\SQLite3\SQLite3Commons.pas@.
This class implements a server over Win32 GDI messages communication, when its {\f1\fs20 ExportServerMessage} method is called.
: Client-Side
A dedicated {\f1\fs20 TSQLRestClientURIMessage} class has been defined. It inherits from {\f1\fs20 TSQLRestClientURI}, and override its {\f1\fs20 URI} protected method so that it communicates using Win32 GDI messages.

[SDD-DI-2.1.1.2.4]
; SRS-DI-2.1.1.2.4 - Client-Server HTTP/1.1 protocol communication
:Implementation
: Server-Side
The communication is not implemented directly in the {\f1\fs20 TSQLRestServer} class, defined in @!TSQLRestClientURI,TSQLRestServer.URI!Lib\SQLite3\SQLite3Commons.pas@, but by a dedicated {\f1\fs20 TSQLite3HttpServer} class defined in @!TSQLite3HttpServer.Create,TSQLite3HttpServer.DBServer,TSQLite3HttpServer.AddServer!Lib\SQLite3\SQLite3HttpServer.pas@.
This class will instantiate a {\f1\fs20 THttpServerGeneric} instance, defined in @!THttpServer.Create,THttpApiServer.Create,THttpServerGeneric.Request,THttpServerGeneric.OnRequest!Lib\SynCrtSock.pas@, which implements a HTTP/1.1 server over TCP/IP communication.
This server is implemented either:
- Via {\f1\fs20 THttpApiServer} for using the fast kernel-mode http.sys server;
- Via {\f1\fs20 THttpServer}, which is an optimized pure Delphi HTTP/1.1 compliant server, using {\i Thread pool} to reduce resources, and provide best possible performance in user land.
You can register several {\f1\fs20 TSQLRestServer} instance to the same HTTP server, via its {\f1\fs20 AddServer} method.  Each {\f1\fs20 TSQLRestServer} class must have an unique {\f1\fs20 Model.Root} value, to identify which instance must handle a particular request from its URI root string.
A dedicated property, named {\f1\fs20 DBServer}, is an array to all registered {\f1\fs20 TSQLRestServer} instances, which are used to process any request, and answer to it by using the corresponding {\f1\fs20 URI} method - via the {\f1\fs20 OnRequest} standard event prototype.
: Client-Side
A dedicated {\f1\fs20 TSQLite3HttpClient} class has been defined in @!TSQLite3HttpClient.Create!Lib\SQLite3\SQLite3HttpClient.pas@. It inherits from {\f1\fs20 TSQLRestClientURI}, and override its {\f1\fs20 URI} protected method so that it communicates using HTTP/1.1 protocol over TCP/IP, according to the supplied HTTP address name.
By default, {\f1\fs20 TSQLite3HttpClient} maps to a {\f1\fs20 TSQLite3HttpClientWinHTTP} class, which was found out to perform well on most configurations and networks (whereas {\f1\fs20 TSQLite3HttpClientWinSock} should be a bit faster on a local computer).

[SDD-DI-2.1.2]
; SRS-DI-2.1.2 - UTF-8 JSON format must be used to communicate
:Implementation
The JSON parsing and producing is implemented in the @!TTextWriter.Create,TTextWriter.AddJSONEscape,IsJSONString,JSONDecode,JSONEncode,JSONEncodeArray,GetJSONField,JSON_CONTENT_TYPE!Lib\SynCommons.pas@ and @!TSQLTable.GetJSONValues,TSQLTableJSON.Create,TSQLTableJSON.UpdateFrom,TJSONWriter.Create,TSQLRecord.CreateJSONWriter,TSQLRecord.GetJSONValues,GetJSONObjectAsSQL,UnJSONFirstField!Lib\SQLite3\SQLite3Commons.pas@ units.
The JSON encoding and decoding is handled at diverse levels:
- With some JSON-dedicated functions and classes;
- At the database record level;
- At the database request table level.
: JSON-dedicated functions and classes
The main class for producing JSON content is {\f1\fs20 TJSONWriter}. This class is a simple writer to a Stream, specialized for the JSON format. Since it makes
use of an internal buffer, and avoid most temporary {\f1\fs20 string} allocation ({\i e.g.} using the stack instead of a temporary {\f1\fs20 string} via {\f1\fs20 IntToStr()} when converting a numerical value to text), it is much faster than a string append (standard Delphi {\f1\fs20 string := string+string} clauses) to produce its content. In particular, its {\f1\fs20 AddJSONEscape} method will handle JSON content escape, according to the official JSON RFC - see @http://www.ietf.org/rfc/rfc4627.txt paragraph 2.5, directly into the destination buffer. It was also designed to scales well on multi-core sytems.
................................................................................
!  Inc(Len,length(result));
!end;
This code will create a string for each key/value in {\f1\fs20 Fields2[]} and {\f1\fs20 Values[]} arrays, but only once, with the definitive value (even single quote escape and BLOB un-serialize from Base-64 encoding are performed directly from the JSON buffer).

[SDD-DI-2.1.3]
; SRS-DI-2.1.3 - The framework must use an innovative ORM (Object-relational mapping) approach, based on classes RTTI (Runtime Type Information)
:Implementation
Some Delphi @*RTTI@ (Runtime Type Information) objects and classes are implemented in the @!TClassProp,TClassType,TEnumType,TTypeInfo,TSQLRecord.ClassProp,TSQLRecord.GetJSONValues,TPropInfo.GetValue,TPropInfo.SetValue,TSQLRecordProperties!Lib\SQLite3\SQLite3Commons.pas@ unit. The {\i Synopse mORMot Framework} uses this custom functions and objects in order to access to the Delphi @*RTTI@.
The generic functions supplied by the standard {\f1\fs20 TypInfo.pas} unit where not found to be easy to use: there are some record types from one hand, which details the internal @*RTTI@ memory layout generated by the compiler, and there are some functions on the other hand. So the framework unified both RTTI memory layout and methods by defining some {\f1\fs20 object} types (i.e. not Delphi classes, but raw objects which can map directly the RTTI memory layout via a {\f1\fs20 pointer}) with some methods dedicated for RTTI handling and @*ORM@. These {\f1\fs20 object} types are {\f1\fs20 TClassProp, TClassType, TEnumType, TTypeInfo} and {\f1\fs20 TPropInfo}.
Since this ORM is the core of the framework, the code of most of these objects has been tuned for performance: quit all of the methods have two versions in the framework, one in pure pascal code (easy to maintain and understand, and @*64 bit@ compatible), and one in optimized i386 assembler.
As a result, ORM code based on RTTI is fairly easy to use. See for example who a database field index is retrieved for a {\f1\fs20 TSQLRecord} class:
!function ClassFieldIndex(ClassType: TClass; const PropName: shortstring): integer;
!var P: PPropInfo;
!    CP: PClassProp;
!begin
................................................................................
The {\f1\fs20 GarbageCollector} is a global {\f1\fs20 TObjectList}, which is used to store some global instances, living the whole process time, just like our {\f1\fs20 TSQLRecordProperties} values.
A per-class {\f1\fs20 TSQLRecordProperties} was made therefore available for each kind of {\f1\fs20 TSQLRecord} class.
Even most sophisticated methods of the @*ORM@ (like {\f1\fs20 TSQLRecord. GetJSONValues}) make use of these low-level {\f1\fs20 object} types. In most cases, the {\f1\fs20 GetValue} and {\f1\fs20 SetValue} methods of the {\f1\fs20 TPropInfo object} are used to convert any field value stored inside the current {\f1\fs20 TSQLRecord} instance in or from UTF-8 encoded text.

[SDD-DI-2.2.1]
; SRS-DI-2.2.1 - The {\i SQLite3} engine must be embedded to the framework
:Implementation
It's worth noting that the {\i Synopse SQLite3 database engine}, whatever its name states, is not bound to {\i SQLite3} (you can use another database engine for data storage, for example we provide a {\f1\fs20 TSQLRestServerStaticInMemory} class which implements a fast but limited in-memory database engine). Therefore, the {\i SQLite3} engine itself is not implemented in the @!TSQLRestServer!Lib\SQLite3\SQLite3Commons.pas@ unit, but in dedicated units.
The {\i SQLite3} engine is accessed at two levels:
- A low-level direct access to the {\i SQLite3} library, implemented in @!TSQLRequest.Execute,TSQLDataBase,TSQLTableDB.Create!Lib\SynSQLite3.pas@;
- A high-level access, implementing a Client-Side or Server-Side native {\f1\fs20 TSQLRest} descendant using the {\i SQLite3} library for its data persistence, in @!TSQLRestServerDB,TSQLRestClientDB!Lib\SQLite3\SQLite3.pas@.
: Low-Level access to the library
:  Compilation of the SQLite3 engine
First of all, the original source code of the library, which is retrieved from the official {\i SQLite3} web site in the form of the optimized Amalgamation file - see @http://www.sqlite.org/amalgamation.html - is compiled using the free Borland C++ command-line compiler.
&Here are the defines used for this compilation:
&//#define SQLITE_ENABLE_FTS3
&//  this unit is FTS3-ready, but not compiled with it by default
&//  if you don't use FTS3, dont define this conditional: you'll spare 50KB of code
................................................................................
Some Delphi classes are introduced to manage all calls and statements to C-language interface to {\i SQLite}, mapping all {\f1\fs20 sqlite3_*} functions and methods to object-oriented methods.
The @!TSQLTableDB,TSQLRequest,TSQLDataBase,TSQLBlobStream,ESQLException!Lib\SynSQLite3.pas@ unit defines the following classes:
- {\f1\fs20 ESQLException} is a custom {\i SQLite3} dedicated Exception type;
- {\f1\fs20 TSQLDataBase} is a simple wrapper for direct {\i SQLite3} database manipulation;
- {\f1\fs20 TSQLRequest} encapsulates a {\i SQLite3} request;
- {\f1\fs20 TSQLTableDB} executes a @*SQL@ statement in the local {\f1\fs20 SQLite3} database engine, and get result in memory, as JSON content;
- {\f1\fs20 TSQLBlobStream} is available to access to a {\i SQLite3} BLOB Stream.
Those database access types are then used by the following Client-Server @*REST@ful classes, to implement {\i SQLite3} storage for persistence of our @*ORM@ (the so called objects hibernation) in @!TSQLRestClientDB,TSQLRestServerDB!Lib\SQLite3\SQLite3.pas@:
- {\f1\fs20 TSQLRestClientDB} implements a REST client with direct access to a {\i SQLite3} database, that is without the Client-Server aspect of the framework;
- {\f1\fs20 TSQLRestServerDB} can be used to implement a REST server using {\i SQLite3} as its storage engine.

[SDD-DI-2.2.2]
; SRS-DI-2.2.2 - The framework libraries, including all its {\i SQLite3} related features, must be tested using Unitary testing
:Implementation
Some @*test@s classes have been developed, which methods cover most aspect of the framework:
................................................................................
\TTestCompression\TSynTestCase
\TTestClientServerAccess\TSynTestCase
\TTestBigTable\TSynTestCase
\TTestBasicClasses\TSynTestCase
\TSynTestCase\TSynTest
rankdir=LR;
\
Those classes are implemented in @!TTestLowLevelCommon!Lib\SynCommons.pas@, @!TTestLowLevelTypes,TTestBasicClasses!Lib\SQLite3\SQLite3Commons.pas@, @!TTestSQLite3Engine,TTestFileBased,TTestMemoryBased,TTestFileBasedWAL!Lib\SQLite3\SQLite3.pas@ and @!TTestClientServerAccess!Lib\SQLite3\SQLite3HttpServer.pas@ units.

[SDD-DI-2.2.3]
; SRS-DI-2.2.3 - The framework shall be able to access any external database, via OleDB or direct access for Oracle (OCI) or SQLite3 (for external database files)
:SynDB classes
The @SAD@ document detailed the architecture, and main implementation part of the database-agnostic features of the framework.
:Faster late binding
For both our {\i SynDB} and {\i SynBigTable} units, we allow {\i late-binding} of data row values, using a variant and direct named access of properties. It's a very convenient way of accessing result rows values.
................................................................................
:SynFile main Demo
The @SAD-SynFile@ section of the associated @SAD@ has already detailed the architecture and the code used to produce a full featured application, including the User Interface generation.
Please refer to these pages for sample code and general explanation about this feature of the framework.

[SDD-DI-2.3.1.1]
; SRS-DI-2.3.1.1 - Database Grid Display, providing data in the Client Application
:Implementation
A standard {\f1\fs20 TDrawGrid} can be associated to a {\f1\fs20 TSQLTable} instance by using a {\f1\fs20 TSQLTableToGrid} object, as defined in the @!TSQLTableToGrid.Create!Lib\SQLite3\SQLite3UI.pas@:
- Just call {\f1\fs20 TSQLTableToGrid.Create(Grid,Table)} to initiate the association;
- The Table will be released when no longer necessary;
- Any former association by {\f1\fs20 TSQLTableToGrid.Create()} will be overridden;
- Handle Unicode, auto column size, field sort, incremental key lookup, optional hide ID;
- {\i Ctrl + click} on a cell to display its full Unicode content.
For instance, here is how the @!TSQLLister.Create!Lib\SQLite3\SQLite3ToolBar.pas@ unit creates a grid for every {\f1\fs20 TSQLRecord} class it refers to:
!constructor TSQLLister.Create(aOwner: TComponent; aClient: TSQLRestClientURI;
!  (...)
!!  fTableToGrid := TSQLTableToGrid.From(fGrid);
!  if fTableToGrid=nil then begin
!    // this Grid has no associated TSQLTableToGrid -> create default one
!    if fClient.InheritsFrom(TSQLRestClientURI) then
!      C := TSQLRestClientURI(fClient) else
................................................................................
:Implementation
: Rendering
The current implementation of the framework User Interface generation handles two kind of rendering:
- Native VCL components;
- Proprietary TMS components.
You can select which set of components are used, by defining - globally to your project (i.e. in the {\i Project/Options/Conditionals} menu) - the {\f1\fs20 USETMSPACK} conditional. If it is not set (which is by default), it will use VCL components.
: Ribbon-like toolbars
As stated by the @SAD-SynFile@ section of the associated @SAD@, ribbon-like toolbars can be generated by using the {\f1\fs20 TSQLRibbon} class, as defined in @!TSQLRibbon.Create,TSQLRibbonTab,TSQLLister,TSQLCustomToolBar.Init!Lib\SQLite3\SQLite3ToolBar.pas@.
This class will use one {\f1\fs20 TSQLRibbonTab} instance per {\f1\fs20 TSQLRecord} class type it handles, displayed on its own ribbon page, with an associated {\f1\fs20 TDrawGrid} instance and a {\f1\fs20 TGDIPages} report, via a corresponding {\f1\fs20 TSQLLister} instance. Parameters provided from code to the {\f1\fs20 TSQLRibbon. Create} method can customize the toolbar content on purpose. Actions will be provided as an enumeration type, and button captions will be extracted by {\i Un @*Camel@ Casing} of each @*enumerate@d value, using @*RTTI@.
: Stand-alone toolbars
A {\f1\fs20 TSQLCustomToolBar} object can be used to create some generic toolbars, with just some icons and actions on screen, with no reference to any associated {\f1\fs20 TSQLRecord} class. See for instance this sample code:
!procedure TMainLogView.FormCreate(Sender: TObject);
!begin
!  FToolBar.Init(self,TypeInfo(TLogViewAction),ActionClick,ImageList,'');
!  FToolBar.AddToolBar('Test')
!end;
The above lines will create a panel on the owner form, with a toolbar containing one button per each {\f1\fs20 TLogViewAction} element. Icons will be taken from the supplied {\f1\fs20 ImageList} component, and the {\f1\fs20 ActionClick} event handler will be called when a button is pressed.

[SDD-DI-2.3.1.3]
; SRS-DI-2.3.1.3 - Internationalization (i18n) of the whole User Interface
:Implementation
The @!TLanguage,TLanguageFile.Create,S2U,U2S,TLanguageFile.StringToUTF8,TLanguageFile.TimeToText,TLanguageFile.DateToText,TLanguageFile.DateTimeToText,TLanguageFile.UTF8ToString,TLanguageFile.Translate,_!Lib\SQLite3\SQLite3i18n.pas@ unit is able to handle both Internationalization (i18n) and Localization (L10n).
The {\f1\fs20 TLanguageFile} class is able to retrieve a custom list of text, and use it for all {\f1\fs20 resourcestring} and screen captions. The global {\f1\fs20 _()} function, or the {\f1\fs20 Translate} method of the {\f1\fs20 TLanguageFile} class can be used to translate any English text into the corresponding language.
The generic {\f1\fs20 string} type is used when some text is to be displayed on screen. Dedicated {\f1\fs20 U2S} and {\f1\fs20 S2U} functions, or even better the {\f1\fs20 UTF8ToString} and {\f1\fs20 StringToUTF8} methods of a {\f1\fs20 TLanguageFile} instance can be used for proper conversion.
Localization is performed via some dedicated methods of the {\f1\fs20 TLanguageFile} class, like {\f1\fs20 DateToText, DateTimeToText, TimeToText}.

[SDD-DI-2.3.2]
; SRS-DI-2.3.2 - A reporting feature, with full preview and export as PDF or TXT files, must be integrated
:Implementation
The @!TGDIPages!Lib\SQLite3\SQLite3Pages.pas@ unit implements a reporting component named {\f1\fs20 TGDIPages}, with full preview and {\f1\fs20 txt/pdf} export.
Anti-aliased drawing is using the @!TGDIPlus.DrawAntiAliased!Lib\SynGdiPlus.pas@ unit, and the {\f1\fs20 TGDIPlus. DrawAntiAliased} method.
The pdf export itself is implemented via the @!TPdfDocument,TPdfCanvas.RenderMetaFile!Lib\SynPdf.pas@ unit, via a {\f1\fs20 TPdfDocument} component: every page content (in fact, a {\f1\fs20 TMetaFile} instance) is rendered via the {\f1\fs20 TPdfCanvas. RenderMetaFile} method.

[VV]
Owner=SRS
Order=SRS
DisplayName=V&V Plan







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|
|







 







|







 







|







 







|



|







 







|







 







|












|







 







|







 







|







 







|







 







|
|








|







 







|







 







|







 







|







 







|
|
|
|







 







|
|







 







|
>







 







|
|
|
|
|




|





|
|
|
|

|
|







 







|
|







 







|







 







|







 







|







 







|
|







 







|







 







|







 







|







 







|


|







 







|







 







|




|
|

|
|
|
|
|
|
|
|
|
|
|
|







 







|





|

|







 







|







 







|







 







|













|







 







|







 







|







 







|





|



|







 







|









|







 







|







 







|





|







 







|













|









|








|







|
|




|







 







|







 







|


|







 







|







 







|







 







|





|







 







|













|







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
...
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
....
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
....
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
....
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
....
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
....
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
....
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
....
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
....
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
....
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
....
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
....
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
....
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
....
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
....
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
....
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
....
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
....
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
....
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
....
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
....
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
....
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
....
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
....
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
....
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
....
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
....
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
....
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
....
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
....
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
....
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
....
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
....
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
....
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
....
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
....
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
....
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
6256
6257
6258
6259
6260
....
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
....
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
6336
....
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
....
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
....
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
....
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
6566
....
6826
6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840
6841
6842
6843
6844
6845
6846
....
6851
6852
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867
6868
6869
6870
6871
6872
6873
6874
6875
6876
6877
6878
6879
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
....
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
7034
....
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
7147
7148
7149
7150
7151
7152
....
7206
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
....
7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
7244
....
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
....
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
NoConfidential=Yes
; so that no "Confidential" text will appear in page footer - seems convenient for a GPL document ;)
HeaderWithLogo=Yes
; custom page header with the synopse logo

{\b Document License}
THE ATTACHED DOCUMENTS DESCRIBE INFORMATION RELEASED BY SYNOPSE INFORMATIQUE UNDER A GPL 3.0 LICENSE.
{\i Synopse mORMot Framework Documentation}.\line Copyright (C) 2008-2012 Arnaud Bouchez.\line Synopse Informatique - @http://synopse.info
This document is free document; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
The {\i Synopse mORMot Framework Documentation} is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this documentation. If not, see @http://www.gnu.org/licenses
{\b Trademark Notice}
Rather than indicating every occurrence of a trademarked name as such, this document uses the names only in an editorial fashion and to the benefit of the trademark owner with no intention of infringement of the trademark.

[Pictures]
................................................................................
;In the following sections, the {\i Synopse mORMot Framework} library architecture is detailed as:
;\SourcePage

[SAD-Main]
SourcePath=Lib\SQLite3
IncludePath=Lib;Lib\SQLite3
;Lib\SQLite3\Samples\MainDemo
SourceFile=TestSQL3.dpr;mORMot.pas;mORMoti18n.pas;mORMotToolBar.pas;mORMotUI.pas;mORMotUIEdit.pas;mORMotUILogin.pas;mORMotPages.pas;mORMotUIOptions.pas;mORMotUIQuery.pas;mORMotService.pas;mORMotSQLite3.pas;mORMotHttpClient.pas;mORMotHttpServer.pas;SynSQLite3.pas;SynDB.pas;SynOleDB.pas;SynDBOracle.pas;SynDBSQLite3.pas;SynDBODBC.pas;mORMotReport.pas;mORMotVCL.pas;mORMotDB.pas;mORMotFastCGIServer
;Samples\MainDemo\SynFile.dpr
Version=1.18
DisplayName=mORMot Framework

:3Object-Relational Mapping
In order to implement @13@ in our framework, generic access to the data is implemented by defining high-level objects as {\i Delphi} classes, descendant from the {\f1\fs20 @**TSQLRecord@} class.
In our @*Client-Server@ @**ORM@, those classes can be used for at least three main purposes:
................................................................................
!    property SerialNumber: RawUTF8 index 30 read fSerialNumber write fSerialNumber
!!     stored AS_UNIQUE;
!    property Model: TSQLDiaperModel read fModel write fModel;
!    property Baby: TSQLBaby read fBaby write fBaby;
!end;
:  Text fields
Note that {\f1\fs20 WideString, shortstring, UnicodeString} (i.e. Delphi 2009/2010/XE/XE2/XE3 generic string), and indexed properties are not handled yet (use faster {\f1\fs20 RawUnicodeString} instead of {\f1\fs20 WideString} or {\f1\fs20 UnicodeString}). In fact, the generic {\f1\fs20 string} type is handled (as {\f1\fs20 UnicodeString} under Delphi 2009/2010/XE/XE2/XE3), but you may loose some content if you're working with pre-Unicode version of Delphi (in which {\f1\fs20 string = AnsiString} with the current system code page). So we won't recommend its usage.
The natural Delphi type to be used for TEXT storage in our framework is {\f1\fs20 @**RawUTF8@}. All business process should be made using {\f1\fs20 RawUTF8} variables and methods (you have all necessary functions in {\f1\fs20 SynCommons.pas}), then you should explicitly convert the {\f1\fs20 RawUTF8} content into a string using {\f1\fs20 U2S / S2U} from {\f1\fs20 mORMoti18n.pas} or {\f1\fs20 StringToUTF8 / UTF8ToString} which will handle proper char-set conversion according to the current @*i18n@ settings.
For additional information about @*UTF-8@ handling in the framework, see @32@.
:  Date and time fields
For {\f1\fs20 @**TTimeLog@ / @**TModTime@ / @**TCreateTime@}, the proprietary fast {\f1\fs20 Int64} date time format will map the {\f1\fs20 Iso8601} record type, as defined in {\f1\fs20 SynCommons}:
- 0..5 bits will map seconds,
- 6..11 bits will map minutes,
- 12..16 bits will map hours,
- 17..21 bits will map days (minus one),
................................................................................
!  TSQLDest = class(TSQLRecordSigned)
!  published
!    property SignatureTime;
!    property Signature;
!  end;
When a {\f1\fs20 TSQLRecordMany} published property exists in a {\f1\fs20 TSQLRecord}, it is initialized automatically during {\f1\fs20 TSQLRecord.Create} constructor execution into a real class instance. Note that the default behavior for a {\f1\fs20 TSQLRecord} published property is to contain an {\f1\fs20 INTEGER} value which is the ID of the corresponding record - creating a "one to one" or "many to one" relationship. But {\f1\fs20 TSQLRecordMany} is a special case. So don't be confused! :)
This {\f1\fs20 TSQLRecordMany} instance is indeed available to access directly the pivot table records, via {\f1\fs20 FillMany} then {\f1\fs20 FillRow, FillOne} and {\f1\fs20 FillRewind} methods to loop through records, or {\f1\fs20 FillManyFromDest} / {\f1\fs20 DestGetJoined} for most advanced usage.
Here is how the regression @*test@s are written in the {\f1\fs20 SynSelfTests} unit:
!procedure TestMany(aClient: TSQLRestClient);
!var MS: TSQLSource;
!    MD, MD2: TSQLDest;
!    i: integer;
!    sID, dID: array[1..100] of Integer;
!    res: TIntegerDynArray;
!begin
................................................................................
!    Check(MS.DestList.Dest.SignatureTime=MD.fSignatureTime);
!    Check(MS.DestList.Dest.Signature=FormatUTF8('% %',[aClient.ClassName,i]));
!  end;
!!  MS.FillClose;
Note that in our case, an explicit call to {\f1\fs20 FillClose} has been added in order to release all {\f1\fs20 Dest} instances created in {\f1\fs20 FillPrepareMany}. This call is not mandatory if you call {\f1\fs20 MS.Free} directly, but it is required if the same {\f1\fs20 MS} instance is about to use some regular many-to-many methods, like {\f1\fs20 MS.DestList.ManySelect()} - it will prevent any GPF exception to occur with code expecting the {\f1\fs20 Dest} property not to be an instance, but a {\f1\fs20 pointer(DestID)} value.
: Calculated fields
It is often useful to handle some calculated fields. That is, having some field values computed when you set another field value. For instance, if you set an error code from an enumeration (stored in an INTEGER field), you may want the corresponding text (to be stored on a TEXT field). Or you may want a total amount to be computed automatically from some detailed records.
This should not be done on the Server side. In fact, the framework expects the transmitted JSON transmitted from client to be set directly to the database layer, as stated by this code from the {\f1\fs20 mORMotSQLite3} unit:
!function TSQLRestServerDB.EngineUpdate(Table: TSQLRecordClass; ID: integer;
!  const SentData: RawUTF8): boolean;
!begin
!  if (self=nil) or (Table=nil) or (ID<=0) then
!    result := false else begin
!    // this SQL statement use :(inlined params): for all values
!    result := EngineExecuteFmt('UPDATE % SET % WHERE RowID=:(%):;',
................................................................................
- Don't think "@*SQL@", think about classes;
- Don't wonder "How will I store it", but "Which data do I need".
For instance, don't be tempted to always create a pivot table (via a {\f1\fs20 @*TSQLRecordMany@} property), but consider using a {\i @*dynamic array@}, {\f1\fs20 @*TPersistent@, @*TStrings@} or {\f1\fs20 @*TCollection@} @*published properties@ instead.
Or consider that you can use a {\f1\fs20 TRecordReference} property pointing to any registered class of the {\f1\fs20 @*TSQLModel@}, instead of creating one {\f1\fs20 @*TSQLRecord@} property per potential table.
:  Objects, not tables
With an @*ORM@, you should usually define fewer tables than in a "regular" relational database, because you can use the high-level type of the {\f1\fs20 @*TSQLRecord@} properties to handle some per-row data.
The first point, which may be shocking for a database architect, is that you should better {\ul not} create @*Master/Detail@ tables, but just one "master" object with the details stored within, as @*JSON@, via {\i @*dynamic array@}, {\f1\fs20 @*TPersistent@, @*TStrings@} or {\f1\fs20 @*TCollection@} properties.
Another point is that a table is not to be created for every aspect of your software configuration. Let's confess that some DB architects design one configuration table per module or per data table. In an ORM, you could design a configuration class, then use the unique corresponding table to store all configuration encoded as some JSON data, or some DFM-like data. And do not hesitate to separate the configuration from the data, for all not data-related configuration - see e.g. how the {\f1\fs20 mORMotOptions} unit works. With our framework, you can serialize directly any {\f1\fs20 @*TSQLRecord@} or {\f1\fs20 TPersistent} instance into JSON, without the need of adding this {\f1\fs20 TSQLRecord} to the {\f1\fs20 @*TSQLModel@} list. Since revision 1.13 of the framework, you can even define {\f1\fs20 TPersistent} @published properties@ in your {\f1\fs20 TSQLRecord} class, and it will be automatically serialized as TEXT in the database.
:  Methods, not SQL
At first, you should be tempted to write code as such (this code sample was posted on our forum, and is not bad code, just not using the @*ORM@ orientation of the framework):
!  DrivesModel := CreateDrivesModel();
!  GlobalClient := TSQLRestClientDB.Create(DrivesModel, CreateDrivesModel(), 'drives.sqlite', TSQLRestServerDB);
!  TSQLRestClientDB(GlobalClient).Server.DB.Execute(
!    'CREATE TABLE IF NOT EXISTS drives ' +
!    '(id INTEGER PRIMARY KEY, drive TEXT NOT NULL UNIQUE COLLATE NOCASE);');
................................................................................
! begin
!!  GlobalClients.OneFieldValues(TSQLDrives,'drive','',aList);
! end;
The whole query is made in one line, with no {\f1\fs20 SELECT} statement to write.
For a particular ID range, you may have written, with a specific WHERE clause using a @*prepared@ statement:
!!  GlobalClients.OneFieldValues(TSQLDrives,'drive',
!!    'ID>=? AND ID<=?',[],[aFirstID,aLastID],aList);
It's certainly worth reading all the (verbose) interface part of the {\f1\fs20 mORMot.pas} unit, e.g. the {\f1\fs20 TSQLRest} class, to make your own idea about all the high-level methods available. In the following pages, you'll find all needed documentation about this particular unit. Since our framework is used in real applications, most useful methods should already have been made available. If you need additional high-level features, feel free to ask for them, if possible with source code sample, in our forum, freely available at @http://synopse.info
:  Think multi-tier
And do not forget the framework is able to have several level of objects, thanks to our @*Client-Server@ architecture - see @6@. Such usage is not only possible, but strongly encouraged.
You should have business-logic level objects at the Client side. Then both business-logic and DB objects at the Server side.
If you have a very specific database schema, business-logic objects can be of very high level, encapsulating some @*SQL@ views for reading, and accessed via some @*REST@ful @*service@ commands for writing - see @11@.
Another possibility to access your high-level type, is to use either custom {\i SQLite3} @*SQL function@s either @*stored procedure@s - see @23@ - both coded in Delphi.
:38 ORM Cache
Here is the definition of "cache", as stated by {\i Wikipedia}:
................................................................................
In order to make this easy, a dedicated set of classes are available in the {\f1\fs20 SynCommons.pas} unit, and allow to define both filtering and validation. They all will be children of any of those both classes:
\graph HierTSynFilter Filtering and Validation classes hierarchy
\TSynValidate\TSynFilterOrValidate
\TSynFilter\TSynFilterOrValidate
\
{\f1\fs20 @*TSQLRecord@} field content validation is handled in the new {\f1\fs20 TSQLRecord. Validate} virtual method, or via some {\f1\fs20 TSQLValidate} classes.
{\f1\fs20 TSQLRecord} field content filtering is handled in the new {\f1\fs20 TSQLRecord. Filter} virtual method, or via some {\f1\fs20 TSQLFilter} classes.
Some "standard" classes are already defined in the {\f1\fs20 SynCommons.pas} and {\f1\fs20 mORMot.pas} units, to be used for most common usage:
\graph HierTSynFilters Default filters and Validation classes hierarchy
\TSynValidatePassWord\TSynValidateText
\TSynValidateText\TSynValidate
\TSynValidateTableUniqueField\TSynValidateTable
\TSynValidateTable\TSynValidate
\TSynValidateUniqueField\TSynValidateRest
\TSynValidateRest\TSynValidate
................................................................................
\TSynFilterLowerCaseU\TSynFilter
\TSynFilterLowerCase\TSynFilter
\TSynFilter\TSynFilterOrValidate
rankdir=LR;
\
You have powerful validation classes for IP Address, Email (with TLD+domain name), simple {\i regex} pattern, textual validation, strong password validation...
Note that some database-related filtering are existing, like {\f1\fs20 TSynValidateUniqueField} which inherits from {\f1\fs20 TSynValidateRest}.
Of course, the {\f1\fs20 mORMotUIEdit} unit now handles {\f1\fs20 @*TSQLRecord@} automated filtering (using {\f1\fs20 TSQLFilter} classes) and validation (using one of the {\f1\fs20 TSQLValidate} classes).
The unique field validation is now in {\f1\fs20 TSQLRecord. Validate} and not in {\f1\fs20 mORMotUIEdit} itself (to have a better multi-tier architecture).
To initialize it, you can add some filters/validators to your {\f1\fs20 @*TSQLModel@} creation function:
!function CreateFileModel(Owner: TSQLRest): TSQLModel;
!var Classes: array[0..high(FileTabs)] of TSQLRecordClass;
!    i: integer;
!begin
!  for i := 0 to high(FileTabs) do
!    Classes[i] := FileTabs[i].Table;
................................................................................
!  result.Owner := Owner;
!  result.SetActions(TypeInfo(TFileAction));
!  result.SetEvents(TypeInfo(TFileEvent));
!  TSQLFile.AddFilterOrValidate('Name',TSQLFilterLowerCase);
!  TSQLUser.AddFilterOrValidate('Email',TSQLValidateEmail);
!end;
In order to perform the filtering of some content, you'll have to call the {\f1\fs20 aRecord.Filter()} method, and {\f1\fs20 aRecord.Validate()} to test for valid content.
For instance, this is how {\f1\fs20 mORMotUIEdit.pas} unit filters and validates the user interface input:
!procedure TRecordEditForm.BtnSaveClick(Sender: TObject);
! (...)
!  // perform all registered filtering
!!  Rec.Filter(ModifiedFields);
!  // perform content validation
!  FieldIndex := -1;
!!  ErrMsg := Rec.Validate(Client,ModifiedFields,@FieldIndex);
................................................................................
- {\f1\fs20 TSQLDatabase} can cache the last results for SELECT statements, or use a tuned client-side or server-side per-record caching, in order to speed up most read queries, for lighter web server or client User Interface e.g.;
- User @*authentication@ handling ({\i SQLite3} is user-free designed);
- {\i SQLite3} source code was compiled without thread mutex: the caller has to be @*thread-safe@ aware; this is faster on most configurations, since mutex has to be acquired once): low level {\f1\fs20 sqlite3_*()} functions are not thread-safe, as {\f1\fs20 TSQLRequest} and {\f1\fs20 TSQLBlobStream} which just wrap them; but {\f1\fs20 TSQLDataBase} is thread-safe, as {\f1\fs20 TSQLTableDB}/{\f1\fs20 TSQLRestServerDB}/{\f1\fs20 TSQLRestClientDB} which call {\f1\fs20 TSQLDataBase};
- Compiled with {\f1\fs20 SQLITE_OMIT_SHARED_CACHE} define, since with the new @*Client-Server@ approach of this framework, no concurrent access could happen, and an internal efficient caching algorithm is added, avoiding most call of the {\i SQLite3} engine in multi-user environment (any @*AJAX@ usage should benefit of it);
- The embedded {\i SQLite3} database engine can be easily updated from the official {\i SQLite3} source code available at @http://sqlite.org - use the amalgamation C file with a few minor changes (documented in the {\f1\fs20 SynSQLite3.pas} unit) - the resulting C source code delivered as {\f1\fs20 .obj} is also available in the official {\i Synopse} source code repository.
The overhead of including {\i SQlite3} in your server application will be worth it: just some KB to the executable, but with so many nice features, even if only external databases are used.
:  Extended by SQLite3 virtual tables
Since the framework is truly object oriented, another database engine could be used instead of the framework. You could easily write your own {\f1\fs20 TSQLRestServer} descendant (as an example, we included a fast in-memory database engine as {\f1\fs20 @*TSQLRestServerFullMemory@}) and link to a another engine (like {\i FireBird}, or a private one). You can even use our framework without any link to the {\i @*SQLite3@} engine itself, via our provided very fast in memory dataset (which can be made persistent by writing and reading @*JSON@ files on disk). The {\i SQLite3} engine is implemented in a separate unit, named {\f1\fs20 SynSQLite3.pas}, and the main unit of the framework is {\f1\fs20 mORMot.pas}. A bridge between the two units is made with {\f1\fs20 mORMotSQLite3.pas}, which will found our ORM framework using {\i SQLite3} as its core.
The framework ORM is able to access any database class (internal or external), via the powerful {\i SQLite3} Virtual Table mechanisms - see @20@. For instance, any external database (via @*OleDB@ / @*ODBC@ providers or direct {\i @*Oracle@} connection) can be accessed via our {\f1\fs20 @*SynDB@}-based dedicated units, as stated @27@.
As a result, the framework has several potential database back-ends, in addition to the default {\i SQLite3} file-based engine. Each engine may have its own purpose, according to the application expectations.
:59  Data access benchmark
On an average desktop computer, depending on the backend database interfaced, {\i mORMot} excels in speed:
- You can persist up to 150,000 objects per second, or retrieve  240,000 objects per second (for our pure Delphi in-memory engine);
- When data is retrieved from server or client @38@, you can read 450,000 objects per second;
- With a high-performance database like Oracle and our direct access classes, you can write 53,000 and read 72,000 objects per second, over a 100 MB network.
................................................................................
|{\b All Virtual}|167095|162956|168651|253292|118203|97083|90592|94688|56639|52764
|{\b All Direct}|167123|144250|168577|254284|256383|170794|165601|168856|88342|75999
|%
When declared as virtual table (via a {\f1\fs20 VirtualTableRegister} call), you have the full power of SQL (including JOINs) at hand, with incredibly fast CRUD operations: 100,000 requests per second for objects read and write, including serialization and Client-Server communication!
In the above list, the {\i MS SQL Server} is not integrated, but may be used instead of {\i Oracle} (minus the fact that BULK insert is not implemented yet for it, whereas @*array bind@ing boosts {\i Oracle} writing BATCH process performance by 100 times). Any other @*OleDB@ or @*ODBC@ providers may also be used, with direct access (without {\i DBExpress} / {\i BDE} layer nor heavy {\f1\fs20 TDataSet} instance).
Note that all those tests were performed locally and in-process, via a {\f1\fs20 TSQLRestClientDB} instance. For both insertion and reading, a Client-Server architecture (e.g. using HTTP/1.1 for {\i mORMot} clients) will give even better results for BATCH and retrieve all modes. During the tests, internal caching - see @37@ and @38@ - was disabled, so you may expect speed enhancements for real applications, when data is more read than written: for instance, when an object is retrieved from the cache, you achieve 450,000 read requests per second, whatever database is used.
: SQLite3 implementation
Beginning with the revision 1.15 of the framework, the {\i @**SQLite3@} engine itself has been separated from our {\f1\fs20 mORMotSQLite3.pas} unit, and defined as a stand-alone unit named {\f1\fs20 SynSQLite3.pas}. See @SDD-DI-2.2.1@.
It can be used therefore:
- Either stand-alone with direct access of all its features, even using its lowest-level C API, via {\f1\fs20 SynSQLite3.pas} - but you won't be able to switch to another database engine easily;
- Either stand-alone with high-level SQL access, using our {\f1\fs20 @*SynDB@} generic access classes, via {\f1\fs20 SynDBSQLite3.pas} - so you will be able to change to any other database engine (e.g. MSSQL or @*Oracle@) when needed;
- Either Client-Server based access with all our @*ORM@ features - see {\f1\fs20 mORMotSQLite3.pas}.
We'll define here some highlights specific to our own implementation of the {\i SQLite3} engine, and let you consult the official documentation of this great Open Source project at @http://sqlite.org for general information about its common features.
:14  Prepared statement
In order to speed up the time spent in the {\i SQLite3} engine (it may be useful for high-end servers), the framework is able to natively handle @*prepared@ @*SQL@ statements.
Starting with version 1.12 of the framework, we added an internal SQL statement @*cache@ in the database access, available for all SQL request. Previously, only the one-record SQL {\f1\fs20 SELECT * FROM ... WHERE RowID=...} was prepared (used e.g. for the {\f1\fs20 @*TSQLRest@. Retrieve} method).
That is, if a previous SQL statement is run with some given parameters, a prepared version, available in cache, is used, and new parameters are bounded to it before the execution by {\i SQLite3}.
In some cases, it can speed the {\i SQLite3} process a lot. From our profiling, prepared statements make common requests (i.e. select / insert / update on one row) at least two times faster, on an in-memory database ({\f1\fs20 ':memory:'} specified as file name).
In order to use this statement caching, any SQL statements must have the parameters to be surrounded with '{\f1\fs20 :(}' and '{\f1\fs20 ):}'. The SQL format was indeed enhanced by adding an optional way of marking parameters inside the SQL request, to enforce statement preparing and caching.
................................................................................
will execute the following @*SQL@ statement:
$ SELECT MapData.ID From MapData, MapBox WHERE MapData.ID=MapBox.ID
$  AND minX>=:(-81.0): AND maxX<=:(-79.6): AND minY>=:(35.0): AND :(maxY<=36.2):
$  AND MapBox_in(MapData.BlobField,:('\uFFF0base64encoded-81,-79.6,35,36.2'):);
The {\f1\fs20 MapBox_in} @*SQL function@ is registered in {\f1\fs20 @*TSQLRestServerDB@. Create} constructor for all {\f1\fs20 TSQLRecordRTree} classes of the current database model. Both {\f1\fs20 BlobToCoord} and {\f1\fs20 ContainedIn} class methods are used to handle the box storage in the BLOB. By default, it will process a raw {\f1\fs20 array of double}, with a default box match (that is {\f1\fs20 ContainedIn} method will match the simple {\f1\fs20 minX>=...maxY<=...} where clause).
:8  FTS3/FTS4
@**FTS@3/FTS4 are {\i @*SQLite3@} @*virtual table@ modules that allow users to perform full-text searches on a set of documents. The most common (and effective) way to describe full-text searches is "what Google, Yahoo and Altavista do with documents placed on the World Wide Web". Users input a term, or series of terms, perhaps connected by a binary operator or grouped together into a phrase, and the full-text query system finds the set of documents that best matches those terms considering the operators and groupings the user has specified. See @http://www.sqlite.org/fts3.html as reference material about FTS3 usage in {\i SQLite3}.
Since version 1.5 of the framework, the {\f1\fs20 sqlite3fts3.obj} file is always available in the distribution file: just define the {\f1\fs20 INCLUDE_FTS3} conditional globaly for your application (it is expected e.g. in {\f1\fs20 mORMotSQLite3.pas} and {\f1\fs20 SynSQLite3.pas}) to enable {\i FTS3} in your application.
Leave it undefined if you do not need this feature, and will therefore spare some KB of code.
FTS3 and FTS4 are nearly identical. FTS4 is indeed an enhancement to FTS3, added with SQLite version 3.7.4, and included in the release v.1.11 of the framework. They share most of their code in common, and their interfaces are the same. The differences are:
- FTS4 contains query performance optimizations that may significantly improve the performance of full-text queries that contain terms that are very common (present in a large percentage of table rows).
- FTS4 supports some additional options that may used with the {\f1\fs20 matchinfo()} function.
Because it stores extra information on disk in two new shadow tables in order to support the performance optimizations and extra {\f1\fs20 matchinfo()} options, FTS4 tables may consume more disk space than the equivalent table created using FTS3. Usually the overhead is 1-2% or less, but may be as high as 10% if the documents stored in the FTS table are very small. The overhead may be reduced by using a {\f1\fs20 TSQLRecordFTS3} table type instead of {\f1\fs20 TSQLRecordFTS4} declaration, but this comes at the expense of sacrificing some of the extra supported {\f1\fs20 matchinfo()} options.
:   Dedicated FTS3/FTS4 record type
In order to allow easy use of the @*FTS@ feature, some types have been defined:
................................................................................
- One cannot run {\f1\fs20 ALTER TABLE ... ADD COLUMN} commands against a virtual table.
- Particular virtual table implementations might impose additional constraints. For example, some virtual implementations might provide read-only tables. Or some virtual table implementations might allow {\f1\fs20 INSERT} or {\f1\fs20 DELETE} but not {\f1\fs20 UPDATE}. Or some virtual table implementations might limit the kinds of {\f1\fs20 UPDATE}s that can be made.
Example of virtual tables, already included in the {\i SQLite3} engine, are @*FTS@ or @*RTREE@ tables.
Our framework introduces new types of custom virtual table. You'll find classes like {\f1\fs20 @*TSQLVirtualTableJSON@} or {\f1\fs20 @*TSQLVirtualTableBinary@} which handle in-memory data structures. Or it might represent a view of data on disk that is not in the {\i SQLite3} format (e.g. {\f1\fs20 TSQLVirtualTableLog}). It can be used to access any external database, just as if they were native {\i SQLite3} tables - see @27@. Or the application might compute the content of the virtual table on demand.
Thanks to the generic implementation of Virtual Table in {\i SQLite3}, you can use such tables in your SQL statement, and even safely execute a {\f1\fs20 SELECT} statement with {\f1\fs20 JOIN} or custom functions, mixing normal {\i SQLite3} tables and any other Virtual Table. From the @*ORM@ point of view, virtual tables are just tables, i.e. they inherit from {\f1\fs20 TSQLRecordVirtual}, which inherits from the common base {\f1\fs20 TSQLRecord} class.
:  Virtual Table module classes
A dedicated mechanism has been added to the framework, beginning with revision 1.13, in order to easily add such virtual tables with pure Delphi code.
In order to implement a new @*Virtual Table@ type, you'll have to define a so called {\i Module} to handle the fields and data access and an associated {\i Cursor} for the {\f1\fs20 SELECT} statements. This is implemented by the two {\f1\fs20 TSQLVirtualTable} and {\f1\fs20 TSQLVirtualTableCursor} classes as defined in the @!TSQLVirtualTable,TSQLVirtualTableCursor,TSQLVirtualTableJSON,TSQLVirtualTableBinary,TSQLVirtualTableLog,TSQLVirtualTableCursorLog,TSQLVirtualTableCursorJSON,TSQLVirtualTableCursorIndex!Lib\SQLite3\mORMot.pas@ unit.
For instance, here are the default Virtual Table classes deriving from those classes:
\graph HierTSQLVirtualTable Virtual Tables classes hierarchy
\TSQLVirtualTableJSON\TSQLVirtualTable
\TSQLVirtualTableBinary\TSQLVirtualTableJSON
\TSQLVirtualTableLog\TSQLVirtualTable
\TSQLVirtualTableCursorIndex\TSQLVirtualTableCursor
\TSQLVirtualTableCursorJSON\TSQLVirtualTableCursorIndex
\TSQLVirtualTableCursorLog\TSQLVirtualTableCursorIndex
\
{\f1\fs20 @*TSQLVirtualTableJSON@, @*TSQLVirtualTableBinary@} and {\f1\fs20 TSQLVirtualTableCursorJSON} classes will implement a Virtual Table using a {\f1\fs20 @*TSQLRestServerStaticInMemory@} instance to handle fast in-memory @*static@ databases. Disk storage will be encoded either as UTF-8 @*JSON@ (for the {\f1\fs20 TSQLVirtualTableJSON} class, i.e. the '{\f1\fs20 JSON}' module), either in a proprietary @*SynLZ@ compressed format (for the {\f1\fs20 TSQLVirtualTableBinary} class, i.e. the '{\f1\fs20 Binary}' module). File extension on disk will be simply {\f1\fs20 .json} for the '{\f1\fs20 JSON}' module, and {\f1\fs20 .data} for the '{\f1\fs20 Binary}' module. Just to mention the size on disk difference, the 502 KB {\f1\fs20 People.json} content (as created by included regression tests) is stored into a 92 KB {\f1\fs20 People.data} file, in our proprietary optimized format.
Note that the virtual table module name is retrieved from the class name. For instance, the {\f1\fs20 TSQLVirtualTableJSON} class will have its module named as 'JSON' in the SQL code.
To handle external databases, two dedicated classes, named {\f1\fs20 TSQLVirtualTableExternal} and {\f1\fs20 TSQLVirtualTableCursorExternal} will be defined in a similar manner - see @%%HierExternalTables@ @30@.
As you probably have already stated, all those Virtual Table mechanism is implemented in {\f1\fs20 mORMot.pas}. Therefore, it is independent from the {\i SQLite3} engine, even if, to my knowledge, there is no other SQL database engine around able to implement this pretty nice feature.
:  Defining a Virtual Table module
Here is how the {\f1\fs20 TSQLVirtualTableLog} class type is defined, which will implement a @*Virtual Table@ module named "{\f1\fs20 Log}". Adding a new module is just made by overriding some Delphi methods:
!  TSQLVirtualTableLog = class(TSQLVirtualTable)
!  protected
!    fLogFile: TSynLogFile;
!  public
!    class procedure GetTableModuleProperties(
................................................................................
- {\f1\fs20 TQuery} {\i emulation class}, for direct re-use with existing code, in replacement to the deprecated BDE technology;
- Free {\f1\fs20 @*SynDBExplorer@} tool provided, which is a small but efficient way of running queries in a simple User Interface, about all available engines; it is also a good sample program of a stand-alone usage of those libraries.
:   Data types
Of course, our ORM does not need a whole feature set (do not expect to use this database classes with your VCL DB RAD components), but handles directly the basic SQL column types, as needed by our ORM (derived from SQLite's internal column types): {\f1\fs20 NULL, Int64, Double, @*Currency@, DateTime, @*RawUTF8@} and {\f1\fs20 BLOB}.
They are defined as such in {\f1\fs20 @*SynDB@}:
!  TSQLDBFieldType =
!    (ftUnknown, ftNull, ftInt64, ftDouble, ftCurrency, ftDate, ftUTF8, ftBlob);
Those types will map low-level database-level access types, not high-level Delphi types as {\f1\fs20 TSQLFieldType} defined in {\f1\fs20 mORMot.pas}, or the generic huge {\f1\fs20 TFieldType} as defined in the standard VCL {\f1\fs20 DB.pas} unit. In fact, it is more tied to the standard {\i @*SQLite3@} generic types, i.e. NULL, INTEGER, REAL, TEXT, BLOB (with the addition of a {\f1\fs20 ftCurrency} and {\f1\fs20 ftDate} type, for better support of most DB engines) see @http://www.sqlite.org/datatype3.html.
You can note that the only {\f1\fs20 string} type handled here uses UTF-8 encoding (implemented using our {\f1\fs20 RawUTF8} type), for cross-Delphi true Unicode process. Code can access to the textual data via {\f1\fs20 variant, string} or {\f1\fs20 widestring} variables and parameters, but our units will use UTF-8 encoding internally. It will therefore interface directly with our ORM, which uses the same encoding.
BLOB columns or parameters are accessed as {\f1\fs20 RawByteString} variables, which may be mapped to a standard {\f1\fs20 TStream} via our {\f1\fs20 TRawByteStringStream}.
:   SynDB Units
Here are the units implementing the external database-agnostic features:
|%30%70
|\b File|Description\b0
|{\f1\fs20 SynDB}|abstract database direct access classes
................................................................................
!  end;
!end;
As you can see, there is no difference with using the local {\i SQLite3} engine or a remote database engine. From the Client point of view, you just call the usual RESTful methods, i.e. {\f1\fs20 Add / Retrieve / Update / UnLock / Delete}, and you can even handle advanced methods like a {\f1\fs20 FillPrepare} with a complex WHERE clause, or {\f1\fs20 CreateSQLMultiIndex / CreateMissingTables} on the server side. Even the creation of the table in the remote database (the {\f1\fs20 'CREATE TABLE...'} SQL statement) is performed by the framework, with the appropriate column properties according to the database expectations (e.g. a {\f1\fs20 TEXT} for {\i SQLite3} will be a {\f1\fs20 NVARCHAR2} field for {\i Oracle}).
The only specific instruction is the global {\f1\fs20 @*VirtualTableExternalRegister@} function, which has to be run on the server side (it does not make any sense to run it on the client side, since for the client there is no difference between any tables - in short, the client do not care about storage; the server does). In order to work as expected, {\f1\fs20 VirtualTableExternalRegister()} shall be called {\i before} {\f1\fs20 TSQLRestServer.Create} constructor: when the server initializes, the ORM server must know whenever an {\i internal} or {\i external} database shall be managed.
Note that in the above code, the {\f1\fs20 LastChange} field was defined as a {\f1\fs20 TModTime}: in fact, the current date and time will be stored each time the record is updated, i.e. for each {\f1\fs20 fClient.Add} or {\f1\fs20 fClient.Update} calls. This is tested by both {\f1\fs20 RExt.LastChange>=Now} and {\f1\fs20 RExt.LastChange<=Now} checks in the latest loop. The time used is the "server-time", i.e. the current time and date on the server (not on the client), and, in the case of external databases, the time of the remote server (it will execute e.g. a {\f1\fs20 select getdate()} under @*MS SQL@ to synchronize the date to be inserted for {\f1\fs20 LastChange}). In order to retrieve this server-side time stamp, we use {\f1\fs20 Now := fClient.ServerTimeStamp} instead of the local {\f1\fs20 Iso8601Now} function.
A similar feature is tested for the {\f1\fs20 CreatedAt} published field, which was defined as {\f1\fs20 TCreateTime}: it will be set automatically to the current server time at record creation (and not changed on modifications). This is the purpose of the {\f1\fs20 RExt.CreatedAt<=Now} check in the above code.
:30   Behind the scene
The {\f1\fs20 mORMotDB.pas} unit, introduced in revision 1.15, implements @*Virtual Table@s access for any {\f1\fs20 @*SynDB@}-based external database for the framework.
In fact, the new {\f1\fs20 TSQLRestServerStaticExternal, TSQLVirtualTableCursorExternal} and {\f1\fs20 TSQLVirtualTableExternal} classes will implement this feature:
\graph HierExternalTables External Databases classes hierarchy
\TSQLRecordVirtual\TSQLRecord
\TSQLRecordVirtualTableAutoID\TSQLRecordVirtual
\TSQLVirtualTableCursorExternal\TSQLVirtualTableCursor
\TSQLVirtualTableExternal\TSQLVirtualTable
\
................................................................................
!!  aDB.UseCache := true; // we better use caching in this JSON oriented use
!  (...)
This will enable a global JSON cache at the SQL level. This cache will be reset on every {\f1\fs20 INSERT, UPDATE} or {\f1\fs20 DELETE} SQL statement, whatever the corresponding table is.
In practice, this global cache was found to be efficient, even if its implementation is some kind of "naive". It is in fact much more tuned than other HTTP-level caching mechanisms used in most client-server solutions (using e.g. a {\i Squid} proxy) - since our caching is at the SQL level, it is shared among all @*CRUD@ / @*Rest@ful queries, and is also indenpendent from the authentication scheme, which pollutes the URI. Associated with the other levels of cache - see @38@ - the framework scaling was found to be very good.
:9  REST
:   RESTful implementation
{\i Representational state transfer} (@**REST@) is a style of software architecture for distributed hypermedia systems such as the World Wide Web. As such, it is not just a method for building "web @*service@s". The terms "representational state transfer" and "REST" were introduced in 2000 in the doctoral dissertation of Roy Fielding, one of the principal authors of the Hypertext Transfer Protocol (@**HTTP@) specification, on which the whole Internet rely.
The {\i Synopse mORMot Framework} was designed in accordance with Fielding's REST architectural style without using HTTP and without interacting with the World Wide Web. Such Systems which follow REST principles are often referred to as "RESTful". Optionally, the Framework is able to serve standard HTTP/1.1 pages over the Internet (by using the {\f1\fs20 mORMotHttpClient / mORMotHttpServer} units and the {\f1\fs20 TSQLHttpServer} and {\f1\fs20 TSQLHttpClient} classes), in an embedded low resource and fast HTTP server.
The standard RESTful methods are implemented:
- GET to list the members of the collection;
- PUT to update a member of the collection;
- POST to create a new entry in the collection;
- DELETE to delete a member of the collection.
The following methods were added to the standard REST definition, for locking individual records and for handling database @*transaction@s (which speed up database process):
- LOCK to lock a member of the collection;
................................................................................
Some dedicated methods of the generic {\f1\fs20 @*TSQLRest@} class handle BLOB fields: {\f1\fs20 RetrieveBlob} and {\f1\fs20 UpdateBlob}.
:   REST and JSON
The "{\i 04 - @*HTTP@ @*Client-Server@}" sample application available in the framework source code tree can be used to show how the framework is @*AJAX@-ready, and can be proudly compared to any other @*REST@ server (like {\i CouchDB}) also based on {\f1\fs20 JSON}.
First desactivate the authentication - see @18@ - by changing the parameter from {\f1\fs20 true} to {\f1\fs20 false} in {\f1\fs20 Unit2.pas}:
! DB := TSQLRestServerDB.Create(Model,ChangeFileExt(paramstr(0),'.db3'),
!! false);
and by commenting the following line in {\f1\fs20 Project04Client.dpr}:
!  Form1.Database := TSQLHttpClient.Create(Server,'8080',Form1.Model);
!!  // TSQLHttpClient(Form1.Database).SetUser('User','synopse');
!  Application.Run;
Then you can use your browser to test the JSON content:
- Start the {\f1\fs20 Project04Server.exe} program: the background HTTP server, together with its {\i SQLite3} database engine;
- Start any {\f1\fs20 Project04Client.exe} instances, and add/find any entry, to populate the database a little;
- Close the {\f1\fs20 Project04Client.exe} programs, if you want;
- Open your browser, and type into the address bar:
$  http://localhost:8080/root
- You'll see an error message:
$TSQLHttpServer Server Error 400
- Type into the address bar:
$  http://localhost:8080/root/SampleRecord
- You'll see the result of all {\f1\fs20 SampleRecord} IDs, encoded as a JSON list, e.g.
$ [{"ID":1},{"ID":2},{"ID":3},{"ID":4}]
- Type into the address bar:
$  http://localhost:8080/root/SampleRecord/1
- You'll see the content of the {\f1\fs20 SampleRecord} of ID=1, encoded as JSON, e.g.
................................................................................
!begin
!  FParent := Value;
!end;
In Delphi, most common kind of reference-copy variables (i.e. {\f1\fs20 variant}, {\i dynamic array} or {\f1\fs20 string}) solve this issue by implementing {\i copy-on-write}. Unfortunately, this pattern is not applicable to {\f1\fs20 interface}, which are not value objects, but reference objects, tied to an implementation {\f1\fs20 class}, which can't be copied.
One common solution is to use {\i Weak pointers}, by which the {\f1\fs20 interface} is assigned to a property without incrementing the reference count.
Note that garbage collector based languages (like Java or C#) do not suffer from this problem, since the circular references are handled by their memory model: objects lifetime are maintained globally by the memory manager. Of course, it will increase memory use, slowdown the process due to additional actions during allocation and assignments (all objects and their references have to be maintained in internal lists), and may slow down the application when garbage collector enters in action. In order to avoid such issues when performance matters, experts tend to pre-allocate and re-use objects: this is one common limitation of this memory model, and why Delphi is still a good candidate (like unmanaged C or C++ - and also {\i Objective C}) when it deals with performance and stability. In some cases (e.g. when using an object cache), such languages have to introduce some kind of "weak pointers", to allow some referenced objects to be reclaimed by garbage collection: but it is a diverse mechanism, under the same naming.
:    Handling weak pointers
In order to easily create a weak pointer, the following function was added to {\f1\fs20 mORMot.pas}:
!procedure SetWeak(aInterfaceField: PIInterface; const aValue: IInterface);
!begin
!  PPointer(aInterfaceField)^ := Pointer(aValue);
!end;
It will assign the `interface` to a field by assigning the `pointer` of this instance to the internal field. It will by-pass the reference counting, so memory won't be leaked any more.
Therefore, it could be used as such:
!procedure TParent.SetChild(const Value: IChild);
................................................................................
!begin
!  SetWeak(@FParent,Value);
!end;
:    Zeroing weak pointers
But there are still some cases where it is not enough. Under normal circumstances, a {\f1\fs20 class} instance should not be deallocated if there are still outstanding references to it. But since weak references don't contribute to an {\f1\fs20 interface} reference count, a {\f1\fs20 class} instance can be released when there are outstanding weak references to it. Some memory leak or even random access violations could occur. A debugging nightmare...
In order to solve this issue, ARC's {\i Zeroing Weak pointers} come to mind.\line It means that weak references will be set to {\f1\fs20 nil} when the object they reference is released. When this happens, the automatic zeroing of the outstanding weak references prevents them from becoming dangling pointers. And {\i voilŕ}! No access violation any more!
Such a {\i Zeroing} ARC model has been implemented in {\i Objective C} by Apple, starting with Mac OS X 10.7 Lion, in replacement (and/or addition) to the previous manual memory handling implementation pattern: in its Apple's flavor, ARC is available not only for interfaces, but for objects, and is certainly more sophisticated than the basic implementation available in the Delphi compiler: it is told (at least from the marketing paper point of view) to use some deep knowledge of the software architecture to provide an accurate access to all instances - whereas the Delphi compiler just relies on a {\i out-of-scope} pattern. In regard to classic {\i garbage collector} memory model, ARC is told to be much more efficient, due to its deterministic nature: Apple's experts ensure that it does make a difference, in term of memory use and program latency - which both are very sensitive on "modest" mobile devices. In short, thanks to ARC, your phone UI won't glitch during background garbage recycling. So {\f1\fs20 mORMot} will try to offer a similar feature, even if the Delphi compiler does not implement it (yet).
In order to easily create a so-called zeroing weak pointer, the following function was defined in {\f1\fs20 mORMot.pas}:
!procedure SetWeakZero(aObject: TObject; aObjectInterfaceField: PIInterface;
!  const aValue: IInterface);
A potential use case could be:
!procedure TParent.SetChild(const Value: IChild);
!begin
!  SetWeakZero(self,@FChild,Value);
!end;
................................................................................
- {\i Andreas Hausladen} provided a classical and complete implementation at @http://andy.jgknet.de/blog/2009/06/weak-interface-references using some nice tricks (like per-instance optional speed up using a void {\f1\fs20 IWeakInterface interface} whose VMT slot will refer to the references list), is thread-safe and is compatible with most Delphi versions - but it will slow down all {\f1\fs20 TObject.FreeInstance} calls (i.e. within {\f1\fs20 Free / Destroy}) and won't allow any overriden {\f1\fs20 FreeInstance} method implementation;
- {\i Vincent Parrett} proposed at @http://www.finalbuilder.com/Resources/Blogs/PostId/410/WeakRefence-in-Delphi-solving-circular-interfac.aspx a {\f1\fs20 generic}-based solution (not thread-safe nor optimized for speed), but requiring to inherit from a base class for any {\f1\fs20 class} that can have a weak reference pointing to it;
- More recently, {\i Stefan Glienke} published at @http://delphisorcery.blogspot.fr/2012/06/weak-interface-references.html another {\f1\fs20 generic}-based solution, not requiring to inherit from a base class, but not thread-safe and suffering from the same limitations related to {\f1\fs20 TObject.FreeInstance}.
The implementation included within {\i mORMot} uses several genuine patterns, when compared to existing solutions:
- It will hack the {\f1\fs20 TObject.FreeInstance} at the {\f1\fs20 class} VMT level, so will only slow down the exact {\f1\fs20 class} which is used as a weak reference, and not others (also its inherited {\f1\fs20 classes} won't be overridden) - and it will allow custom override of the {\f1\fs20 virtual FreeInstance} method;
- It makes use of our {\f1\fs20 TDynArrayHashed} wrapper to provide a very fast lookup of instances and references, without using {\f1\fs20 generic} definitions - hashing will start when it will be worth it, i.e. for any list storing more than 32 items;
- The unused {\f1\fs20 vmtAutoTable} VMT slot is used to handle the class-specific orientation of this feature (similar to {\f1\fs20 TSQLRecordProperties} lookup as implemented for @DI-2.1.3@), for best speed and memory use.
See the {\f1\fs20 TSetWeakZeroClass} and {\f1\fs20 TSetWeakZeroInstance} implementation in {\f1\fs20 mORMot.pas} for the details.
:62   Interfaces in practice: dependency injection, stubs and mocks
In order to fulfill the @47@, two features are to be available when handling interfaces:
- Dependency injection;
- Stubbing and mocking of interfaces for proper testing.
We will show now how {\i mORMot} provides all needed features for such patterns, testing a simple "forgot my password" scenario: a password shall be computed for a given user name, then transmitted via SMS, and its record shall be updated in the database.
:    Dependency injection
A direct implementation of dependency injection at a {\f1\fs20 class} level can be implemented in Delphi as such:
................................................................................
- Named pipes, which can be used locally between a Server running as a Windows service and some Client instances;
- @*HTTP@/1.1 over TCP/IP, for remote access.
See @%%mORMotDesign1@ about this Client-Server architecture.
:  Implementation design
A typical Client-Server @*REST@ful POST / Add request over HTTP/1.1 will be implemented as such, on both Client and Server side:
\graph ArchClient Client-Server implementation - Client side
subgraph cluster {
\Client.Add\TSQLHttpClient.URI\ORM to JSON over REST
\TSQLHttpClient.URI\Http Server\HTTP protocol¤POST
\Http Server\TSQLHttpClient.URI
\TSQLHttpClient.URI\Client.Add\return¤new ID
label = "Client";
}
\
\graph ArchServer Client-Server implementation - Server side
subgraph cluster {
\Http Server\TSQLRestServer.URI\dispatch
\TSQLRestServer.URI\TSQLRestServerDB.EngineAdd\decode¤POST JSON
................................................................................
label = "Server";
}
\
Of course, several clients can access to the same server.
It's possible to by-pass the whole Client-Server architecture, and let the application be stand-alone, by defining a {\f1\fs20 @*TSQLRestClientDB@} class, which will embed a {\f1\fs20 @*TSQLRestServerDB@} instance in the same executable:
\graph ArchStandAlone Client-Server implementation - Stand-Alone application
subgraph cluster {
\Client.Add\TSQLRestClientDB.URI\ORM to JSON over REST
\TSQLRestClientDB.URI\TSQLRestServerDB.URI\direct call
\TSQLRestServerDB.URI\TSQLRestServerDB.EngineAdd\decode¤POST JSON
\TSQLRestServerDB.EngineAdd\SQLite3 SQL\SQL insert
\SQLite3 SQL\SQLite3 BTREE\prepare + execute
\SQLite3 BTREE\Database file\atomic write
\SQLite3 BTREE\TSQLRestServerDB.URI\return new ID
\TSQLRestServerDB.URI\Client.Add\return new ID
label = "Stand-Alone application";
................................................................................
\OleDB/ODBC or other\TSQLDBStatement
\TSQLDBStatement\TSQLVirtualTableExternal
\TSQLVirtualTableExternal\TSQLRestServer.URI\return new ID
\TSQLRestServer.URI\Http Server\return 200 OK + ID
label = "Server";
}
\
In fact, the above function correspond to a database model with only external virtual tables, and with {\f1\fs20 StaticVirtualTableDirect=false}, 
i.e. calling the Virtual Table mechanism of {\i SQlite3} for each request.
Most of the time, i.e. for RESTful / @*CRUD@ commands, the execution is more direct:
\graph ArchVirtualDirect Client-Server implementation - Server side with "static" Virtual Tables
subgraph cluster {
\Http Server\TSQLRestServer.URI\dispatch
\TSQLRestServer.URI\TSQLRestServerDB.EngineAdd\Is a SQLite3 table?
\TSQLRestServer.URI\TSQLRestServerStaticExternal.EngineAdd\Is a static table?
\TSQLRestServerDB.EngineAdd\SQLite3 SQL\SQL insert
................................................................................
\TSQLRestServerRemoteDB\TSQLRestServer
\TSQLRestServerFullMemory\TSQLRestServer
\TSQLRestServer\TSQLRest
\TSQLRestClientURINamedPipe\TSQLRestClientURI
\TSQLRestClientURIMessage\TSQLRestClientURI
\TSQLRestClientURIDll\TSQLRestClientURI
\TSQLRestClientDB\TSQLRestClientURI
\TSQLHttpClientWinINet\TSQLHttpClientWinGeneric
\TSQLHttpClientWinHTTP\TSQLHttpClientWinGeneric
\TSQLHttpClientWinGeneric\TSQLHttpClientGeneric
\TSQLHttpClientWinSock\TSQLHttpClientGeneric
\TSQLHttpClientGeneric\TSQLRestClientURI
\TSQLRestClientURI\TSQLRestClient
\TSQLRestClient\TSQLRest
\
For a stand-alone application, create a {\f1\fs20 @*TSQLRestClientDB@}. This particular class will initialize an internal {\f1\fs20 @*TSQLRestServerDB@} instance, and you'll have full access to the database in the same process, with no speed penalty.
For a @*Client-Server@ application, create a {\f1\fs20 TSQLRestServerDB} instance, then use the corresponding {\f1\fs20 ExportServer, ExportServerNamedPipe, ExportServerMessage} method to instantiate either a in-process, Named-Pipe or GDI message server. For @*HTTP@/1.1 over TCP/IP, creates a {\f1\fs20 TSQLHttpServer} instance, and associate your running {\f1\fs20 TSQLRestServerDB} to it. Then create either a {\f1\fs20 TSQLHttpClient}, {\f1\fs20 TSQLRestClientURIDll, TSQLRestClientURINamedPipe} or {\f1\fs20 TSQLRestClientURIMessage} instance to access to your data according to the communication protocol used for the server.
In practice, in order to implement the business logic, you should better create a new class, inheriting from {\f1\fs20 TSQLRestServerDB}. If your purpose is not to have a full {\i SQLite3} engine available, you may create your server from a {\f1\fs20 @*TSQLRestServerFullMemory@} class instead of {\f1\fs20 TSQLRestServerDB}: this will implement a fast in-memory engine (using {\f1\fs20 TSQLRestServerStaticInMemory} instances), with basic CRUD features (for ORM), and persistence on disk as JSON or optimized binary files - this kind of server is enough to handle authentication, and host @*service@s in a stand-alone way. If your services need to have access to a remote ORM server, it may use a {\f1\fs20 @*TSQLRestServerRemoteDB@} class instead: this server will use an internal {\f1\fs20 TSQLRestClient} instance to handle all ORM operations- it can be used e.g. to host some services on a stand-alone server, with all ORM and data access retrieved from another server: it will allow to easily implement a proxy architecture (for instance, as a DMZ for publishing services, but letting ORM process stay out of scope).
All those classes will implement a RESTful access to a remote database, with associated services and business logic.
:  HTTP client
In fact, there are several implementation of a @**HTTP@/1.1 client, according to this class hierarchy:
\graph ClientRESTClasses HTTP/1.1 Client RESTful classes
\TSQLHttpClientWinINet\TSQLHttpClientWinGeneric
\TSQLHttpClientWinHTTP\TSQLHttpClientWinGeneric
\TSQLHttpClientWinGeneric\TSQLHttpClientGeneric
\TSQLHttpClientWinSock\TSQLHttpClientGeneric
\
So you can select either {\f1\fs20 TSQLHttpClientWinSock}, {\f1\fs20 TSQLHttpClientWinINet} or {\f1\fs20 TSQLHttpClientWinHTTP} for a HTTP/1.1 client.
Each class has its own architecture, and attaches itself to a Windows communication library, all based on {\i WinSock} API. As stated by their name, {\f1\fs20 TSQLHttpClientWinSock} will call directly the {\i WinSock} API, {\f1\fs20 TSQLHttpClientWinINet} will call {\i WinINet} API (as used by IE 6) and {\f1\fs20 TSQLHttpClientWinHTTP} will cal the latest {\i WinHTTP} API:
- {\i WinSock} is the common user-space API to access the sockets stack of Windows, i.e. IP connection - it's able to handle any IP protocol, including TCP/IP, UDP/IP, and any protocol over it (including HTTP);
- {\i WinINet} was designed as an HTTP API client platform that allowed the use of interactive message dialogs such as entering user credentials - it's able to handle HTTP and FTP protocols;
- {\i WinHTTP}'s API set is geared towards a non-interactive environment allowing for use in service-based applications where no user interaction is required or needed, and is also much faster than {\i WinINet} - it only handles HTTP protocol.
\graph ClientHTTPClasses HTTP/1.1 Client architecture
\WinINet\WinSock
\UrlMon\WinINet
\RASAPI\WinINet
................................................................................
|Local speed|Fastest|Slow|Fast
|Network speed|Slow|Medium|Fast
|Minimum OS|Win95/98|Win95/98|Win2000
|HTTPS|Not available|Available|Available
|Integration with IE|None|Excellent (proxy)|Available (see below)
|User interactivity|None|Excellent (authentication, dial-up)|None
|%
As stated above, there is still a potential performance issue to use the direct {\f1\fs20 TSQLHttpClientWinSock} class over a network. It has been reported on our forum, and root cause was not identified yet.
Therefore, the {\f1\fs20 TSQLHttpClient} class maps by default to the {\f1\fs20 TSQLHttpClientWinHTTP} class. This is the recommended usage from a Delphi client application.
Note that even if {\i WinHTTP} does not share by default any proxy settings with Internet Explorer, it can import the current IE settings.  The {\i WinHTTP} proxy configuration is set by either {\f1\fs20 proxycfg.exe} on Windows XP and Windows Server 2003 or earlier, either {\f1\fs20 netsh.exe} on Windows Vista and Windows Server 2008 or later; for instance, you can run "{\f1\fs20 proxycfg -u}" or "{\f1\fs20 netsh winhttp import proxy source=ie}" to use the current user's proxy settings for Internet Explorer. Under @*64 bit@ Vista/Seven, to configure applications using the 32 bit {\i WinHttp} settings, call {\f1\fs20 netsh} or {\f1\fs20 proxycfg} bits from {\f1\fs20 %SystemRoot%\\SysWOW64} folder explicitly.
:  HTTP server using http.sys
:   Presentation
Since {\i Windows XP SP2} and {\i Windows Server 2003}, the Operating System provides a kernel stack to handle @**HTTP@ requests. This {\f1\fs20 http.sys} driver is in fact a full featured HTTP server, running in kernel mode. It is part of the networking subsystem of the {\i Windows} operating system, as a core component.
The {\f1\fs20 SynCrtSock} unit can implement a HTTP server based on this component. Of course, the {\i Synopse mORMot framework} will use it. If it's not available, it will launch our pure Delphi optimized HTTP server, using I/O completion ports and a Thread Pool.
What’s good about https.sys?
- {\i Kernel-mode request queuing}: Requests cause less overhead in context switching, because the kernel forwards requests directly to the correct worker process. If no worker process is available to accept a request, the kernel-mode request queue holds the request until a worker process picks it up.
................................................................................
\ORM HTTP Server\ORM database core\In-process¤no latency
\ORM database core\ORM HTTP Server
\
The BATCH sequence allows you to regroup those statements into just ONE remote call. Internally, it builds a @*JSON@ stream, then post this stream at once to the server. Then the server answers at once, after having performed all the modifications.
Some new {\f1\fs20 TSQLRestClientURI} methods have been added to implement BATCH sequences to speed up database modifications: after a call to {\f1\fs20 BatchStart}, database modification statements are added to the sequence via {\f1\fs20 BatchAdd / BatchUpdate / BatchDelete}, then all statements are sent as one to the remote server via {\f1\fs20 BatchSend} - this is MUCH faster than individual calls to {\f1\fs20 Add / Update / Delete} in case of a slow remote connection (typically @*HTTP@ over Internet).
Since the statements are performed at once, you can't receive the result (e.g. the ID of the added row) on the same time as you append the request to the BATCH sequence. So you'll have to wait for the {\f1\fs20 BatchSend} method to retrieve all results, {\i at once}, in a {\i dynamic} {\f1\fs20 array of integer}.
As you may guess, it's also a good idea to use a @*transaction@ for the whole process. By default, the BATCH sequence is not embedded into a transaction. It's up to the caller to use a {\f1\fs20 TransactionBegin} ... {\f1\fs20 try}... {\f1\fs20 Commit  except RollBack} block.
Here is a typical use (extracted from the regression @*test@s in {\f1\fs20 SynSelfTests.pas}:
!// start the transaction
!!if ClientDist.TransactionBegin(TSQLRecordPeople) then
!try
!  // start the BATCH sequence
!!  Check(ClientDist.BatchStart(TSQLRecordPeople));
!  // delete some elements
!  for i := 0 to n-1 do
................................................................................
!end;
The only not obvious part of this code is the parameters marshaling, i.e. how the values are retrieved from the incoming {\f1\fs20 aParams.Parameters} text buffer, then converted into native local variables.
On the Server side, typical implementation steps are therefore:
- Use the {\f1\fs20 UrlDecodeNeedParameters} function to check that all expected parameters were supplied by the caller in {\f1\fs20 aParams.Parameters};
- Call {\f1\fs20 UrlDecodeInteger / UrlDecodeInt64 / UrlDecodeExtended / UrlDecodeValue / UrlDecodeObject} functions (all defined in {\f1\fs20 SynCommons.pas}) to retrieve each individual parameter from standard JSON content;
- Implement the service (here it is just the {\f1\fs20 a+b} expression);
- Then return the result into {\f1\fs20 aParams.Resp} variable.
The powerful {\f1\fs20 UrlDecodeObject} function (defined in {\f1\fs20 mORMot.pas}) can be used to un-serialize most class instance from its textual JSON representation ({\f1\fs20 @*TPersistent@, @*TSQLRecord@, TStringList}...).
Note that due to this implementation pattern, the {\i mORMot} service implementation is very fast, and not sensitive to the "Hash collision attack" security issue, as reported with {\i Apache} - see @http://blog.synopse.info/post/2011/12/30/Hash-collision-attack for details.
The implementation must return the HTTP error code (e.g. 200 on success) as an integer value, and any response in {\f1\fs20 aParams.Resp} as a serialized JSON object by default (using e.g. {\f1\fs20 TSQLRestServer.JSONEncodeResult}), since default mime-type is {\f1\fs20 JSON_CONTENT_TYPE}:
$ {"Result":"OneValue"}
or a JSON object containing an array:
$ {"Result":["One","two"]}
So you can consume these services, implemented Server-Side in fast Delphi code, with any @*AJAX@ application on the client side (if you use HTTP as communication protocol).
The {\f1\fs20 aParams.Head^} parameter may be overridden on the server side to set a custom header which will be provided to the client - it may be useful for instance to specify another mime-type than the default constant {\f1\fs20 JSON_CONTENT_TYPE}, i.e. {\f1\fs20 'application/json; charset=UTF-8'}, and returns plain text, HTML or binary.
................................................................................
!function Sum(aClient: TSQLRestClientURI; a, b: double): double;
!var err: integer;
!begin
!  val(aClient.CallBackGetResult('sum',['a',a,'b',b]),Result,err);
!end;
You could even implement this method in a dedicated client method - which make sense:
!type
!  TMyClient = class(TSQLHttpClient) // could be TSQLRestClientURINamedPipe
!  (...)
!    function Sum(a, b: double): double;
!  (...)
!
!function TMyClient.Sum(a, b: double): double;
!var err: integer;
!begin
................................................................................
The server is implemented as such:
!program Project14Server;
!
!{$APPTYPE CONSOLE}
!
!uses
!  SysUtils,
!  mORMot,
!  mORMotSQLite3,
!  Project14Interface;
!
!type
!  TServiceCalculator = class(TInterfacedObject, ICalculator)
!  public
!    function Add(n1,n2: integer): integer;
!  end;
................................................................................
- A {\f1\fs20 TSQLRestClientURINamedPipe} instance is created, with an associate {\f1\fs20 TSQLModel} and the given {\f1\fs20 APPLICATION_NAME} to access the proper server via a named pipe communication;
- The connection is authenticated with the default {\f1\fs20 'User'} rights;
- The {\f1\fs20 ICalculator} interface is defined in the client's internal factory, in {\f1\fs20 sicShared} mode (just as in the server).
Once the client is up and ready, the local {\f1\fs20 I: ICalculator} variable instance is retrieved, and the remote service is called directly via a simple {\f1\fs20 I.Add(a,b)} statement.
You can imagine how easy and safe it will be to implement a @17@ for your future applications, using {\i mORMot}.
:   Implementation details
:    Involved classes
You will find out in {\f1\fs20 mORMot.pas} all classes implementing this interface communication.
\graph HierTServiceContainerServer Services implementation classes hierarchy
\TServiceFactoryServer\TServiceFactory
\TServiceFactoryClient\TServiceFactory
\TServiceContainerClient\TServiceContainer
\TServiceContainerServer\TServiceContainer
\
There are two levels of implementation:
................................................................................
|Password on Server|Yes|Yes/No|N/A
|Truly Stateless|Yes|No|Yes
|Truly RESTful|No|No|Yes
|HTTP-free|No|No|Yes
|%
:   HTTP basic auth over HTTPS
This first solution, based on the standard @**HTTPS@ protocol, is used by most web services. It's easy to implement, available by default on all browsers, but has some known draw-backs, like the awful authentication window displayed on the Browser, which will persist (there is no {\i LogOut}-like feature here), some server-side additional CPU consumption, and the fact that the user-name and password are transmitted (over HTTPS) into the Server (it should be more secure to let the password stay only on the client side, during keyboard entry, and be stored as secure hash on the Server).
The supplied {\f1\fs20 TSQLHttpClientWinHTTP} and {\f1\fs20 TSQLHttpClientWinINet} clients classes are able to connect using HTTPS, and the {\f1\fs20 THttpApiServer} server class can send compatible content.
:   Session via Cookies
To be honest, a @**session@ managed on the Server is not truly @*Stateless@. One possibility could be to maintain all data within the cookie content. And, by design, the cookie is handled on the Server side (Client in fact don’t even try to interpret this cookie data: it just hands it back to the server on each successive request). But this cookie data is application state data, so the client should manage it, not the server, in a pure Stateless world.
The cookie technique itself is HTTP-linked, so it's not truly RESTful, which should be protocol-independent. Since our framework does not provide only HTTP protocol, but offers other ways of transmission, Cookies were left at the baker's home.
:   Query Authentication
{\i @**Query Authentication@} consists in signing each RESTful request via some additional parameters on the URI. See @http://broadcast.oreilly.com/2009/12/principles-for-standardized-rest-authentication.html about this technique. It was defined as such in this article:
{\i All REST queries must be authenticated by signing the query parameters sorted in lower-case, alphabetical order using the private credential as the signing token. Signing should occur before URI encoding the query string.}
For instance, here is a generic URI sample from the link above:
................................................................................
Some working @**JavaScript@ code has been published in our forum by a framework user (thanks, "RangerX"), which implements the authentication schema as detailed above. It uses {\f1\fs20 jQuery}, and HTML 5 {\f1\fs20 LocalStorage}, not cookies, for storing session information on the Client side.
See @http://synopse.info/forum/viewtopic.php?pid=2995#p2995
The current revision of the framework contains the code as expected by this JavaScript code - especially the results encoded as @2@ objects.
In the future, some "official" code will be available for such AJAX clients. It will probably rely on pure-pascal implementation using such an {\i Object-Pascal-to-JavaScript} compiler - it does definitively make sense to have Delphi-like code on the client side, not to break the @*ORM@ design. For instance, the Open Source {\f1\fs20 DWS} ({\i DelphiWebScript}) compiler matches our needs - see @http://delphitools.info/tag/javascript
:12 Testing
:25  Thread-safety
On the Server side, our Framework was designed to be @**thread-safe@.
In fact, the {\f1\fs20 TSQLRestServer.URI} method is expected to be thread-safe, e.g. from the {\f1\fs20 TSQLHttpServer. Request} method. Thanks to the @*REST@ful approach of our framework, this method is the only one which is expected to be thread-safe.
In order to achieve this thread-safety without sacrificing performance, the following rules were applied in {\f1\fs20 TSQLRestServer.URI}:
- Most of this methods's logic is to process the incoming parameters, so is thread-safe by design (e.g. {\f1\fs20 Model} and {\f1\fs20 RecordProps} access do not change during process);
- The {\i @*SQLite3@} engine access is protected at SQL/JSON @*cache@ level, via {\f1\fs20 DB.LockJSON()} calls in {\f1\fs20 @*TSQLRestServerDB@} methods;
- {\f1\fs20 TSQLRestServerStatic} main methods ({\f1\fs20 EngineList, EngineRetrieve, EngineAdd, EngineUpdate, EngineDelete, EngineRetrieveBlob, EngineUpdateBlob}) are thread-safe: e.g. {\f1\fs20 @*TSQLRestServerStaticInMemory@} uses a per-Table Critical Section;
- {\f1\fs20 TSQLRestServerCallBack} methods (i.e. @*published method@s of the inherited {\f1\fs20 TSQLRestServer} class) must be implemented to be thread-safe;
- A protected {\f1\fs20 fSessionCriticalSection} is used to protect shared {\f1\fs20 fSession[]} access between clients;
- Remote external tables - see @27@ - use thread-safe connections and statements when accessing the databases via SQL;
................................................................................
- Any exceptions triggered during process via {\f1\fs20 sllException} and {\f1\fs20 sllExceptionOS} levels;
- Client and server @*REST@ful {\f1\fs20 URL} methods via {\f1\fs20 sllClient} and {\f1\fs20 sllServer} levels;
- @*SQL@ executed statements in the {\i @*SQLite3@} engine via the {\f1\fs20 sllSQL} level;
- @*JSON@ results when retrieved from the {\i SQLite3} engine via the {\f1\fs20 sllResult} level;
- Main errors triggered during process via {\f1\fs20 sllError} level;
- @*Security@ User authentication and @*session@ management via {\f1\fs20 sllUserAuth};
- Some additional low-level information via {\f1\fs20 sllDebug} and {\f1\fs20 sllInfo} levels.
Those levels are available via the {\f1\fs20 TSQLLog} class, inheriting from {\f1\fs20 TSynLog}, as defined in @!TSQLLog!Lib\SQLite3\mORMot.pas@.
Three main {\f1\fs20 TSynLogClass} global variables are defined in order to use the same {\f1\fs20 TSynLog} class for all logging available in the framework units. Since all layers are not common, several variables have been defined, as such:
- {\f1\fs20 SynDBLog} for all {\i @*SynDB@*} units, i.e. all generic database code;
- {\f1\fs20 SQLite3Log} for all {\i mORMot*} units, i.e. all @*ORM@ related code;
- {\f1\fs20 SynSQLite3Log} for the {\f1\fs20 SynSQLite3} unit, which implements the {\i @*SQLite3@} engine itself.
For instance, if you execute the following statement at the beginning of {\f1\fs20 TestSQL3.dpr}, most regression @*test@s will produce some logging, and will create about 270 MB of log file content, if executed:
!  with TSQLLog.Family do begin
!    Level := LOG_VERBOSE;
!    HighResolutionTimeStamp := true;
!    TSynLogTestLog := TSQLLog;
!    SynDBLog := TSQLLog;
................................................................................
As retrieved from our source code repository, you'll find the following file layout.
|%30%70
|\b Directory|Description\b0
|{\f1\fs20 /}|Root folder, containing common files
|{\f1\fs20 HtmlView/}|A fork of the freeware {\f1\fs20 THtmlView} component, used as a demo of the {\f1\fs20 SynPdf} unit - not finished, and not truly Unicode ready
|{\f1\fs20 LVCL/}|{\i Light VCL} replacement files for standard VCL (for Delphi 6-7 only)
|{\f1\fs20 RTL7/}|Enhanced RTL .dcu for Delphi 7 (not mandatory at all), and {\i FastMM4} memory manager to be used before Delphi 2006
|{\f1\fs20 SQLite3/}|Contains all @*ORM@ / @*SOA@ related files of the framework (i.e. {\i mORMot} itself)
|{\f1\fs20 SynProject/}|Source code of the {\i SynProject} tool, used to create e.g. this documentation
;|{\f1\fs20 zeos/}|A fork of the freeware {\i Zeos} library - not finished, and not truly Unicode ready
|%
In the {\i Root folder}, some common files are defined:
|%30%70
|\b File|Description\b0
|{\f1\fs20 CPort.*}|A fork of the freeware {\i ComPort} Library ver. 2.63
................................................................................
|\b File|Description\b0
|{\f1\fs20 @*SynDB@}|abstract database direct access classes
|{\f1\fs20 SynOleDB}|fast @*OleDB@ direct access classes
|{\f1\fs20 SynDBODBC}|fast @*ODBC@ direct access classes
|{\f1\fs20 SynDBOracle}|{\i @*Oracle@} DB direct access classes (via OCI)
|{\f1\fs20 SynDBSQLite3}|{\i @*SQLite3@} direct access classes
|%
In the {\f1\fs20 SQlite3/} folder, the files defining the {\i Synopse mORMot framework} (using mostly {\f1\fs20 SynCommons, SynLZ, SynSQLite3, SynGdiPlus, SynCrtSock, SynPdf, SynTaskDialog} and {\f1\fs20 SynZip} from the {\i Root folder}):
|%30%70
|\b File|Description\b0
|{\f1\fs20 Documentation/}|Sub folder containing the source of the Synopse documentation
|{\f1\fs20 Samples/}|Sub folders containing some sample code
|{\f1\fs20 mORMot.pas}|Main unit of the @*ORM@ framework
|{\f1\fs20 mORMotSQLite3.pas}|{\i SQLite3} kernel bridge between {\f1\fs20 mORMot.pas} and {\f1\fs20 SynSQLite3.pas}
|{\f1\fs20 *.bmp *.rc}|Resource files, compiled into {\f1\fs20 *.res} files
|{\f1\fs20 mORMotFastCgiServer.pas}|FastCGI server - not fully tested
|{\f1\fs20 mORMotHttpClient.pas}|HTTP/1.1 Client
|{\f1\fs20 mORMotHttpServer.pas}|HTTP/1.1 Server
|{\f1\fs20 mORMotPages.pas}|Integrated Reporting engine
|{\f1\fs20 mORMotService.pas}|Stand-alone Service
|{\f1\fs20 mORMotToolBar.pas}|ORM ToolBar User Interface generation
|{\f1\fs20 mORMotUI.*}|Grid to display Database content
|{\f1\fs20 mORMotUIEdit.*}|Record edition dialog, used to edit record content on the screen
|{\f1\fs20 mORMotUILogin.*}|some common User Interface functions and dialogs
|{\f1\fs20 mORMotUIOptions.*}|General Options setting dialog, generated from code
|{\f1\fs20 mORMotUIQuery.*}|Form handling queries to a User Interface Grid, using our ORM RTTI to define search parameters and algorithms
|{\f1\fs20 mORMoti18n.pas}|internationalization (@*i18n@) routines and classes
|{\f1\fs20 TestSQL3.dpr}|Main unit @*test@ing program of the Synopse {\i mORMot} framework
|{\f1\fs20 TestSQL3Register.dpr}|Run as administrator for {\i TestSQL3} to use {\i http.sys} on Vista/Seven
|{\f1\fs20 c.bat sqlite3.c}|Source code of the {\i SQLite3} embedded Database engine
|%
: Installation
Just unzip the {\f1\fs20 .zip} archive, including all sub-folders, into a local directory of your computer (for instance, {\f1\fs20 D:\Dev\Lib}).
Then add the following paths to your Delphi IDE (in {\i Tools/Environment/Library} menu):
................................................................................
- Enhanced @*log@ging.
:32 Unicode and UTF-8
Our {\i mORMot} Framework has 100% UNICODE compatibility, that is compilation under Delphi 2009/2010/XE/XE2/XE3. The code has been deeply rewritten and @*test@ed, in order to provide compatibility with the {\f1\fs20 String=UnicodeString} paradigm of these compilers.  But the code will also handle safely Unicode for older version, i.e. from Delphi 6 up to Delphi 2007.
Since our framework is natively @**UTF-8@ (this is the better character encoding for fast text - @*JSON@ - streaming/parsing and it is natively supported by the {\i SQLite3} engine), we had to establish a secure way our framework used strings, in order to handle all versions of Delphi (even pre-Unicode versions, especially the Delphi 7 version we like so much), and provide compatibility with the Free Pascal Compiler.
Some string types have been defined, and used in the code for best cross-compiler efficiency (avoiding most conversion between formats):
- {\f1\fs20 @*RawUTF8@} is used for every internal data usage, since both {\i @*SQLite3@} and JSON do expect UTF-8 encoding;
- {\f1\fs20 WinAnsiString} where {\i WinAnsi}-encoded {\f1\fs20 AnsiString} (code page 1252) are needed;
- Generic {\f1\fs20 string} for {\i @*i18n@} (e.g. in unit {\f1\fs20 mORMoti18n}), i.e. text ready to be used within the VCL, as either {\f1\fs20 AnsiString} (for Delphi 2 to 2007) or {\f1\fs20 UnicodeString} (for Delphi 2009 and later);
- {\f1\fs20 RawUnicode} in some technical places (e.g. direct Win32 *W() API call in Delphi 7) - note: this type is NOT compatible with Delphi 2009 and later {\f1\fs20 UnicodeString};
- {\f1\fs20 RawByteString} for byte storage (e.g. for {\f1\fs20 FileFromString()} function);
- {\f1\fs20 SynUnicode} is the fastest available Unicode {\i native} string type, depending on the compiler used (i.e. {\f1\fs20 WideString} before Delphi 2009, and {\f1\fs20 UnicodeString} since);
- Some special conversion functions to be used for Delphi 2009+ {\f1 UnicodeString} (defined inside {\f1\fs20 \{$ifdef UNICODE\}...\{$endif\}} blocks);
- Never use {\f1\fs20 AnsiString} directly, but one of the types above.
Note that {\f1\fs20 RawUTF8} is the preferred {\f1\fs20 string} type to be used in our framework when defining textual properties in a {\f1\fs20 @*TSQLRecord@} and for all internal data processing. It's only when you're reaching the User Interface layer that you may convert explicitly the {\f1\fs20 RawUTF8} content into the generic VCL {\f1\fs20 string} type, using either the {\f1\fs20 Language. UTF8ToString} method (from {\f1\fs20 mORMoti18n.pas} unit) or the following function from {\f1\fs20 SynCommons.pas}:
!/// convert any UTF-8 encoded String into a generic VCL Text
!// - it's prefered to use TLanguageFile.UTF8ToString() in mORMoti18n.pas,
!// which will handle full i18n of your application
!// - it will work as is with Delphi 2009+ (direct unicode conversion)
!// - under older version of Delphi (no unicode), it will use the
!// current RTL codepage, as with WideString conversion (but without slow
!// WideString usage)
!function UTF8ToString(const Text: RawUTF8): string;
Of course, the {\f1\fs20 StringToUTF8} method or function are available to send back some text to the @*ORM@ layer.\line A lot of dedicated conversion functions (including to/from numerical values) are included in {\f1\fs20 SynCommons.pas}. Those were optimized for speed and multi-thread capabilities, and to avoid implicit conversions involving a temporary {\f1\fs20 string} variable.
................................................................................
This code will therefore:
- Save the current register context via {\f1\fs20 pushad / popad} opcodes pair;
- Check if {\f1\fs20 TSynLog} should intercept exceptions (i.e. if the global {\f1\fs20 SynLogExceptionEnabled} boolean is true);
- Call our logging function {\f1\fs20 LogExcept};
- Call the default Windows {\f1\fs20 RtlUnwind} API, as expected by the Operating System.
:  Serialization
{\i @*dynamic array@s} can also be serialized as @*JSON@ in the log on request, via the default {\f1\fs20 TSynLog} class, as defined in {\f1\fs20 SynCommons} unit - see @48@.
The {\f1\fs20 TSQLLog} class (using the enhanced @*RTTI@ methods defined in {\f1\fs20 mORMot.pas} unit) is even able to serialize {\f1\fs20 @*TSQLRecord@, @*TPersistent@, TList} and {\f1\fs20 @*TCollection@} instances as JSON, or any other class instance, after call to {\f1\fs20 TJSONSerializer. @*RegisterCustomSerializer@}.
For instance, the following code:
!procedure TestPeopleProc;
!var People: TSQLRecordPeople;
!    Log: ISynLog;
!begin
!  Log := TSQLLog.Enter;
!  People := TSQLRecordPeople.Create;
................................................................................
You could of course design your own User Interface without our framework. That is, this is perfectly feasible to use only the @*ORM@ part of it. For instance, it should be needed to develop @*AJAX@ applications using its @*REST@ful model - see @9@ - since such a feature is not yet integrated to our provided source code.
But for producing easily applications, the framework provides a mechanism based on both ORM description and @*RTTI@ compiler-generated information in order to create most User Interface by code.
It is able to generated a Ribbon-based application, in which each table is available via a Ribbon tab, and some actions performed to it.
So the framework would need to know:
- Which tables must be displayed;
- Which actions should be associated with each table;
- How the User Interface should be customized (e.g. hint texts, grid layout on screen, reporting etc...);
- How generic automated edition, using the @!TRecordEditForm!Lib\SQLite3\mORMotUIEdit.pas@ unit, is to be generated.
To this list could be added an integrated event feature, which can be linked to actions and custom status, to provide a centralized handling of user-level @*log@ing (as used e.g. in the {\i SynFile} {\f1\fs20 TSQLAuditTrail} table) - please do not make confusion between this user-level logging and technical-level logging using {\f1\fs20 TSynLog} and {\f1\fs20 TSQLLog} classes and "families" - see @16@.
: Rendering
The current implementation of the framework User Interface generation handles two kind of rendering:
- Native VCL components;
- Proprietary TMS components.
You can select which set of components are used, by defining - globally to your project (i.e. in the {\i Project/Options/Conditionals} menu) - the {\f1\fs20 USETMSPACK} conditional. If it is not set (which is by default), it will use VCL components.
The native VCL components will use native Windows API components. So the look and feel of the application will vary depending on the Windows version it is running on. For instance, the resulting screen will be diverse if the application is run under Windows 2000, XP, Vista and Seven. The "ribbon" as generated with VCL components has most functionalities than the Office 2007/2010 ribbon, but will have a very diverse layout.
................................................................................
|Ribbon look|Unusual|Office-like
|Preview button & Shortcuts|None by default|Available
|Extra Price|None|High
|GPL ready|Yes|No
|Office UI Licensing|N/A|Required
|EXE size|Smaller|Bigger
|%
It's worth saying that the choice of one or other component set could be changed on request. If you use the generic components as defined in {\f1\fs20 mORMotToolBar} (i.e. the {\f1\fs20 TSynForm, TSynToolBar, TSynToolButton, TSynPopupMenu, TSynPage, TSynPager, TSynBodyPager} and {\f1\fs20 TSynBodyPage} classes) and {\f1\fs20 SynTaskDialog} (for {\f1\fs20 TSynButton}) in your own code, the {\f1\fs20 USETMSPACK} conditional will do all the magic for you.
The {\i Office UI licensing program} was designed by {\i Microsoft} for software developers who wish to implement the Office UI as a software component and/or incorporate the Office UI into their own applications. If you use TMS ribbon, it will require acceptance of the Office UI License terms as defined at @http://msdn.microsoft.com/en-us/office/aa973809.aspx
If you want to design your user interface using a Office 2007/2010 ribbon look, please take a look at those official guidelines: @http://msdn.microsoft.com/en-us/library/cc872782.aspx
Here is the screen content, using the TMS components:
%synfiletms.png
And here is the same application compiled using only VCL components, available from Delphi 6 up to XE3:
%synfilevcl.png
We did not use yet the Ribbon component as was introduced in Delphi 2009. Its action-driven design won't make it easy to interface with the event-driven design of our User Interface handling, and we have to confess that this component has rather bad reputation (at least in the Delphi 2009 version). Feel free to adapt our Open Source code to use it - we'll be very pleased to release a new version supporting it, but we don't have time nor necessity to do it by ourself.
: Enumeration types
A list of available actions should be defined, as an enumeration type:
!  TFileAction = (
!    faNoAction, faMark, faUnmarkAll, faQuery, faRefresh, faCreate,
!    faEdit, faCopy, faExport, faImport, faDelete, faSign, faPrintPreview,
!    faExtract, faSettings );
Thanks to the Delphi @*RTTI@, and "{\i Un @*Camel@ Casing}", the following list will generate a set of available buttons on the User Interface, named "Mark", "Unmark all", "Query", "Refresh", "Create", "Edit", "Copy", "Export", "Import", "Delete", "Sign", "Print preview", "Extract" and "Settings". Thanks to the @!TLanguageFile.Translate!Lib\SQLite3\mORMoti18n.pas@ unit (responsible of application @*i18n@) and the {\f1\fs20 TLanguageFile. Translate} method, it could be translated on-the-fly from English into the current desired language, before display on screen or report creation.
See both above screen-shots to guess how the button captions match the enumeration names - i.e. @%synfilevcl.png@ and @%synfilevcl.png@.
A list of events, as used for the {\f1\fs20 TSQLAuditTrail} table, was also defined. Some events reflect the change made to the database rows (like {\f1\fs20 feRecordModified}), or generic application status (like {\f1\fs20 feServerStarted}):
!  TFileEvent = (
!    feUnknownState, feServerStarted, feServerShutdown,
!    feRecordCreated, feRecordModified, feRecordDeleted,
!    feRecordDigitallySigned, feRecordImported, feRecordExported );
In the grid and the reports, @*RTTI@ and "{\i uncamelcasing}" will be used to display this list as regular text, like "{\i Record digitally signed}", and translated to the current language, if necessary.
................................................................................
!  /// class used to create the User interface
!  TFileRibbon = class(TSQLRibbon)
!  public
!    /// overriden method used customize the report content
!    procedure CreateReport(aTable: TSQLRecordClass; aID: integer; aReport: TGDIPages;
!      AlreadyBegan: boolean=false); override;
!  end;
The reporting engine in the framework is implemented via the {\f1\fs20 TGDIPages} class, defined in the @!TGDIPages!Lib\SQLite3\mORMotReport.pas@:
- Data is drawn in memory, they displayed or printed as desired;
- High-level reporting methods are available (implementing tables, columns, titles and such), but you can have access to a {\f1\fs20 TCanvas} property which allows any possible content generation via standard VCL methods;
- Allow preview (with anti-aliased drawing via GDI+) and printing;
- Direct export as {\f1\fs20 .txt} or {\f1\fs20 .@*pdf@} file;
- Handle bookmark, outlines and links inside the document.
By default, the {\f1\fs20 CreateReport} method of {\f1\fs20 TSQLRibbon} will write all editable fields value to the content.
The method is overridden by the following code:
................................................................................
!    end;
Report header is written using the following methods:
- {\f1\fs20 DrawTitle} to add a title to the report, with a black line below it (second parameter to {\f1\fs20 true}) - this title will be added to the report global outline, and will be exported as such in {\f1\fs20 .pdf} on request;
- {\f1\fs20 NewHalfLine} and {\f1\fs20 NewLine} will leave some vertical gap between two paragraphs;
- {\f1\fs20 AddColumns}, with parameters set as percentages, will initialize a table with the first column content defined as bold ({\f1\fs20 SetColumnBold(0)});
- {\f1\fs20 DrawTextAcrossCols} and {\f1\fs20 DrawTextAcrossColsFromCSV} will fill a table row according to the text specified, one string per column;
- {\f1\fs20 DrawBMP} will draw a bitmap to the report, which content is loaded using the generic {\f1\fs20 LoadFromRawByteString} function implemented in @!Lib\SynGdiPlus.pas@;
- {\f1\fs20 U2S} and {\f1\fs20 Iso2S} function, as defined in @!Iso2S,U2S!Lib\SQLite3\mORMoti18n.pas@, are used for conversion of some text or {\f1\fs20 @*TTimeLog@} into a text formated with the current language settings (@*i18n@).
!    // write report content
!    DrawTitle(sContent,true);
!    SaveLayout;
!    Font.Name := 'Courier New';
!    if Rec.InheritsFrom(TSQLSafeMemo) then
!      DrawText(sSafeMemoContent) else
!    if Rec.InheritsFrom(TSQLMemo) then
................................................................................
!  sSizeN = 'Size: %s';
!  sContentTypeN = 'Content Type: %s';
!  sSafeMemoContent = 'This memo is password protected.'#13+
!    'Please click on the "Edit" button to show its content.';
!  sDataContent = 'Please click on the "Extract" button to get its content.';
!  sSignedN = 'Signed,By %s on %s';
!  sPictureN = '%s Picture';
The @!Lib\SQLite3\mORMoti18n.pas@ unit is able to parse all those {\f1\fs20 resourcestring} from a running executable, via its {\f1\fs20 ExtractAllResources} function, and create a reference text file to be translated into any handled language.
Creating a report from code does make sense in an ORM. Since we have most useful data at hand as Delphi classes, code can be shared among all kind of reports, and a few lines of code is able to produce complex reports, with enhanced rendering, unified layout, direct internationalization and export capabilities.
: Application i18n and L10n
In computing, internationalization and localization (also spelled internationalisation and localisation) are means of adapting computer software to different languages, regional differences and technical requirements of a target market:
- {\i Internationalization} (@**i18n@) is the process of designing a software application so that it can be adapted to various languages;
- {\i Localization} (@**L10n@) is the process of adapting internationalized software for a specific region or language by adding locale-specific components and translating text, e.g. for dates display.
Our framework handles both features, via the @!Lib\SQLite3\mORMoti18n.pas@ unit. We just saw above how {\f1\fs20 @*resourcestring@} defined in the source code are retrieved from the executable and can be translated on the fly. The unit extends this to visual forms, and even captions generated from @*RTTI@ - see @5@.
The unit expects all textual content (both {\f1\fs20 resourcestring} and RTTI derived captions) to be correct English text. A list of all used textual elements will be retrieved then hashed into an unique numerical value. When a specific locale is set for the application, the unit will search for a {\f1\fs20 @*.msg@} text file in the executable folder matching the expected locale definition. For instance, it will search for {\f1\fs20 FR.msg} for translation into French.
In order to translate all the user interface, a corresponding {\f1\fs20 .msg} file is to be supplied in the executable folder. Neither the source code, nor the executable is to be rebuild to add a new language. And since this file is indeed a plain textual file, even a non developer (e.g. an end-user) is able to add a new language, starting from another {\f1\fs20 .msg}.
:  Creating the reference file
In order to begin a translation task, the {\f1\fs20 mORMoti18n.pas} unit is able to extract all textual resource from the executable, and create a reference text file, containing all English sentences and words to be translated, associated with their numerical hash value.
It will in fact:
- Extract all {\f1\fs20 resourcestring} text;
- Extract all captions generated from RTTI (e.g. from enumerations or class properties names);
- Extract all embedded {\f1\fs20 dfm} resources, and create per-form sections, allowing a custom translation of displayed captions or hints.
This creation step needs a compilation of the executable with the {\f1\fs20 EXTRACTALLRESOURCES} conditional defined, {\i globally} to the whole application (a full {\i rebuild} is necessary after having added or suppressed this conditional from the {\i Project / Options / Folders-Conditionals} IDE field).
Then the {\f1\fs20 ExtractAllResources} global procedure is to be called somewhere in the code.
For instance, here is how this is implemented in @!TMainForm.FormShow!Lib\SQLite3\Samples\MainDemo\FileMain.pas@, for the framework main demo:
................................................................................
$KeyWords.EditLabel.Caption=_3731019706   KeyWords
$
$[TLoginForm]
$Label1.Caption=_1741937413   &User name:
$Label2.Caption=_4235002365   &Password:
$
$[TMainForm]
$Caption=_16479868    Synopse mORMot demo - SynFile
$
$[Messages]
$2784453965=Memo
$2751226180=Data
$744738530=Safe memo
$895337940=Safe data
$2817614158=Name
$1741937413=&User name:
$4235002365=&Password:
$16479868= Synopse mORMot demo - SynFile
$940170664=Content
$3153227598=None
$3708724895=Page %d / %d
$2767358349=Size: %s
$4281038646=Content Type: %s
$2584741026=This memo is password protected.|Please click on the "Edit" button to show its content.
$3011148197=Please click on the "Extract" button to get its content.
................................................................................
!!  i18nLanguageToRegistry(lngFrench);
!  Ribbon.ToolBar.ActivePageIndex := 1;
!end;
Above code will set the main application language as French. At next startup, the content of a supplied {\f1\fs20 SynFileFR.msg} file will be used to translate all screen layout, including all RTTI-generated captions.
Of course, for a final application, you'll need to change the language by a common setting. See {\f1\fs20 i18nAddLanguageItems, i18nAddLanguageMenu} and {\f1\fs20 i18nAddLanguageCombo} functions and procedures to create your own language selection dialog, using a menu or a combo box, for instance.
:  Localization
Take a look at the {\f1\fs20 TLanguageFile} class. After the main language has been set, you can use the global {\f1\fs20 Language} instance in order to localize your application layout.
The {\f1\fs20 mORMoti18n} unit will register itself to some methods of {\f1\fs20 mORMot.pas}, in order to translate the RTTI-level text into the current selected language. See for instance {\f1\fs20 i18nDateText}.

[SAD-Ajax]
SourcePath=
IncludePath=
SourceFile=
Version=1.18
DisplayName=Ajax clients for mORMot
................................................................................
- Synopse work on the framework is distributed without any warranty, according to the chosen license terms;
- This documentation is released under the GPL (GNU General Public License) terms, without any warranty of any kind.
=[GPL]

[SDD-DI-2.1.1]
; SRS-DI-2.1.1 - The framework must be Client-Server oriented
:Implementation
The @*Client-Server@ aspect of the framework is implemented in the @!TSQLRecord,TSQLRest,TSQLRestServer,TSQLRestClientURI,TSQLTableJSON!Lib\SQLite3\mORMot.pas@ unit, with the {\f1\fs20 TSQLRestServer} and {\f1\fs20 TSQLRestClientURI} classes.
Both classes inherit from a generic {\f1\fs20 TSQLRest} class, which implements some generic database access methods and properties (through @*ORM@ model for objects descending from {\f1\fs20 TSQLRecord} or table-based query using {\f1\fs20 TSQLTableJSON}).

[SDD-DI-2.1.1.1]
; SRS-DI-2.1.1.1 - A RESTful mechanism must be implemented
:Implementation
The @*REST@ful mechanism is implemented using the {\f1\fs20 URI} method of both {\f1\fs20 TSQLRestServer} and {\f1\fs20 TSQLRestClientURI} classes, as defined in the @!TSQLRest,TSQLRestServer,TSQLRestClientURI!Lib\SQLite3\mORMot.pas@ unit.
: Server-Side
In the {\f1\fs20 TSQLRestServer} class, the {\f1\fs20 URI} method is defined as {\f1\fs20 public}, and must implement the actual database query or update, according to the REST request:
!function TSQLRestServer.URI(const url, method: RawUTF8; const SentData: RawUTF8;
!      out Resp, Head: RawUTF8; const RestAccessRights: TSQLAccessRights): Int64Rec;
The purpose of this method is to:
- Return internal database state count (used for caching);
- Retrieve URI expecting the RESTful {\f1\fs20 'ModelRoot[/TableName[/ID[/BlobFieldName]]]'} format;
................................................................................

[SDD-DI-2.1.1.2]
; SRS-DI-2.1.1.2 - Commmunication should be available directly in the same process memory, or remotly using Named Pipes, Windows messages or HTTP/1.1 protocols

[SDD-DI-2.1.1.2.1]
; SRS-DI-2.1.1.2.1 - Client-Server Direct communication inside the same process
:Implementation
The in-process communication is implemented by using a global function, named {\f1\fs20 URIRequest} and defined in @!TSQLRestServer,URIRequest,USEFASTMM4ALLOC,TSQLRestClientURIDll.Create!Lib\SQLite3\mORMot.pas@:
!function URIRequest(url, method, SendData: PUTF8Char; Resp, Head: PPUTF8Char): Int64Rec; cdecl;
: Server-Side
This function can be exported from a DLL to remotely access to a {\f1\fs20 TSQLRestServer}, or used in the same process:
- Use {\f1\fs20 TSQLRestServer.ExportServer} to assign a server to this function;
- Return {\i 501 NOT IMPLEMENTED} error if no {\f1\fs20 TSQLRestServer.ExportServer} has been assigned yet;
- Memory for {\f1\fs20 Resp} and {\f1\fs20 Head} parameters are allocated with {\f1\fs20 GlobalAlloc()} Win32 API function: client must release this pointers with {\f1\fs20 GlobalFree()} after having retrieved their content - you can force using the Delphi heap (and {\f1\fs20 GetMem} function which is much faster than {\f1\fs20 GlobalAlloc}) by setting the {\f1\fs20 USEFASTMM4ALLOC} variable to TRUE: in this case, client must release this pointers with {\f1\fs20 Freemem()}.
: Client-Side
The Client should simply use a {\f1\fs20 TSQLRestClientURIDll} instance to access to an exported {\f1\fs20 URIRequest()} function.

[SDD-DI-2.1.1.2.2]
; SRS-DI-2.1.1.2.2 - Client-Server Named Pipe communication
:Implementation
: Server-Side
The communication is implemented by using the {\f1\fs20 TSQLRestServer} class, defined in @!TSQLRestServer.ExportServerNamedPipe,TSQLRestClientURINamedPipe.Create,TSQLRestClientURI!Lib\SQLite3\mORMot.pas@.
This class implements a server over Named Pipe communication, when its {\f1\fs20 ExportServerNamedPipe} method is called.
: Client-Side
A dedicated {\f1\fs20 TSQLRestClientURINamedPipe} class has been defined. It inherits from {\f1\fs20 TSQLRestClientURI}, and override its {\f1\fs20 URI} protected method so that it communicates using a specified Named Pipe.

[SDD-DI-2.1.1.2.3]
; SRS-DI-2.1.1.2.3 - Client-Server Windows Messages communication
:Implementation
Communication using Win32 GDI messages is very handy and efficient on the same computer. It's also perfectly safe, because, by design, it can't be access remotely. Performances for small messages is also excellent. Named pipe could be faster only when bigger messages are transmitted.
: Server-Side
The communication is implemented by using the {\f1\fs20 TSQLRestServer} class, defined in @!TSQLRestClientURI,TSQLRestServer.ExportServerMessage,TSQLRestClientURIMessage.Create!Lib\SQLite3\mORMot.pas@.
This class implements a server over Win32 GDI messages communication, when its {\f1\fs20 ExportServerMessage} method is called.
: Client-Side
A dedicated {\f1\fs20 TSQLRestClientURIMessage} class has been defined. It inherits from {\f1\fs20 TSQLRestClientURI}, and override its {\f1\fs20 URI} protected method so that it communicates using Win32 GDI messages.

[SDD-DI-2.1.1.2.4]
; SRS-DI-2.1.1.2.4 - Client-Server HTTP/1.1 protocol communication
:Implementation
: Server-Side
The communication is not implemented directly in the {\f1\fs20 TSQLRestServer} class, defined in @!TSQLRestClientURI,TSQLRestServer.URI!Lib\SQLite3\mORMot.pas@, but by a dedicated {\f1\fs20 TSQLHttpServer} class defined in @!TSQLHttpServer.Create,TSQLHttpServer.DBServer,TSQLHttpServer.AddServer!Lib\SQLite3\mORMotHttpServer.pas@.
This class will instantiate a {\f1\fs20 THttpServerGeneric} instance, defined in @!THttpServer.Create,THttpApiServer.Create,THttpServerGeneric.Request,THttpServerGeneric.OnRequest!Lib\SynCrtSock.pas@, which implements a HTTP/1.1 server over TCP/IP communication.
This server is implemented either:
- Via {\f1\fs20 THttpApiServer} for using the fast kernel-mode http.sys server;
- Via {\f1\fs20 THttpServer}, which is an optimized pure Delphi HTTP/1.1 compliant server, using {\i Thread pool} to reduce resources, and provide best possible performance in user land.
You can register several {\f1\fs20 TSQLRestServer} instance to the same HTTP server, via its {\f1\fs20 AddServer} method.  Each {\f1\fs20 TSQLRestServer} class must have an unique {\f1\fs20 Model.Root} value, to identify which instance must handle a particular request from its URI root string.
A dedicated property, named {\f1\fs20 DBServer}, is an array to all registered {\f1\fs20 TSQLRestServer} instances, which are used to process any request, and answer to it by using the corresponding {\f1\fs20 URI} method - via the {\f1\fs20 OnRequest} standard event prototype.
: Client-Side
A dedicated {\f1\fs20 TSQLHttpClient} class has been defined in @!TSQLHttpClient.Create!Lib\SQLite3\SQLite3HttpClient.pas@. It inherits from {\f1\fs20 TSQLRestClientURI}, and override its {\f1\fs20 URI} protected method so that it communicates using HTTP/1.1 protocol over TCP/IP, according to the supplied HTTP address name.
By default, {\f1\fs20 TSQLHttpClient} maps to a {\f1\fs20 TSQLHttpClientWinHTTP} class, which was found out to perform well on most configurations and networks (whereas {\f1\fs20 TSQLHttpClientWinSock} should be a bit faster on a local computer).

[SDD-DI-2.1.2]
; SRS-DI-2.1.2 - UTF-8 JSON format must be used to communicate
:Implementation
The JSON parsing and producing is implemented in the @!TTextWriter.Create,TTextWriter.AddJSONEscape,IsJSONString,JSONDecode,JSONEncode,JSONEncodeArray,GetJSONField,JSON_CONTENT_TYPE!Lib\SynCommons.pas@ and @!TSQLTable.GetJSONValues,TSQLTableJSON.Create,TSQLTableJSON.UpdateFrom,TJSONWriter.Create,TSQLRecord.CreateJSONWriter,TSQLRecord.GetJSONValues,GetJSONObjectAsSQL,UnJSONFirstField!Lib\SQLite3\mORMot.pas@ units.
The JSON encoding and decoding is handled at diverse levels:
- With some JSON-dedicated functions and classes;
- At the database record level;
- At the database request table level.
: JSON-dedicated functions and classes
The main class for producing JSON content is {\f1\fs20 TJSONWriter}. This class is a simple writer to a Stream, specialized for the JSON format. Since it makes
use of an internal buffer, and avoid most temporary {\f1\fs20 string} allocation ({\i e.g.} using the stack instead of a temporary {\f1\fs20 string} via {\f1\fs20 IntToStr()} when converting a numerical value to text), it is much faster than a string append (standard Delphi {\f1\fs20 string := string+string} clauses) to produce its content. In particular, its {\f1\fs20 AddJSONEscape} method will handle JSON content escape, according to the official JSON RFC - see @http://www.ietf.org/rfc/rfc4627.txt paragraph 2.5, directly into the destination buffer. It was also designed to scales well on multi-core sytems.
................................................................................
!  Inc(Len,length(result));
!end;
This code will create a string for each key/value in {\f1\fs20 Fields2[]} and {\f1\fs20 Values[]} arrays, but only once, with the definitive value (even single quote escape and BLOB un-serialize from Base-64 encoding are performed directly from the JSON buffer).

[SDD-DI-2.1.3]
; SRS-DI-2.1.3 - The framework must use an innovative ORM (Object-relational mapping) approach, based on classes RTTI (Runtime Type Information)
:Implementation
Some Delphi @*RTTI@ (Runtime Type Information) objects and classes are implemented in the @!TClassProp,TClassType,TEnumType,TTypeInfo,TSQLRecord.ClassProp,TSQLRecord.GetJSONValues,TPropInfo.GetValue,TPropInfo.SetValue,TSQLRecordProperties!Lib\SQLite3\mORMot.pas@ unit. The {\i Synopse mORMot Framework} uses this custom functions and objects in order to access to the Delphi @*RTTI@.
The generic functions supplied by the standard {\f1\fs20 TypInfo.pas} unit where not found to be easy to use: there are some record types from one hand, which details the internal @*RTTI@ memory layout generated by the compiler, and there are some functions on the other hand. So the framework unified both RTTI memory layout and methods by defining some {\f1\fs20 object} types (i.e. not Delphi classes, but raw objects which can map directly the RTTI memory layout via a {\f1\fs20 pointer}) with some methods dedicated for RTTI handling and @*ORM@. These {\f1\fs20 object} types are {\f1\fs20 TClassProp, TClassType, TEnumType, TTypeInfo} and {\f1\fs20 TPropInfo}.
Since this ORM is the core of the framework, the code of most of these objects has been tuned for performance: quit all of the methods have two versions in the framework, one in pure pascal code (easy to maintain and understand, and @*64 bit@ compatible), and one in optimized i386 assembler.
As a result, ORM code based on RTTI is fairly easy to use. See for example who a database field index is retrieved for a {\f1\fs20 TSQLRecord} class:
!function ClassFieldIndex(ClassType: TClass; const PropName: shortstring): integer;
!var P: PPropInfo;
!    CP: PClassProp;
!begin
................................................................................
The {\f1\fs20 GarbageCollector} is a global {\f1\fs20 TObjectList}, which is used to store some global instances, living the whole process time, just like our {\f1\fs20 TSQLRecordProperties} values.
A per-class {\f1\fs20 TSQLRecordProperties} was made therefore available for each kind of {\f1\fs20 TSQLRecord} class.
Even most sophisticated methods of the @*ORM@ (like {\f1\fs20 TSQLRecord. GetJSONValues}) make use of these low-level {\f1\fs20 object} types. In most cases, the {\f1\fs20 GetValue} and {\f1\fs20 SetValue} methods of the {\f1\fs20 TPropInfo object} are used to convert any field value stored inside the current {\f1\fs20 TSQLRecord} instance in or from UTF-8 encoded text.

[SDD-DI-2.2.1]
; SRS-DI-2.2.1 - The {\i SQLite3} engine must be embedded to the framework
:Implementation
It's worth noting that the {\i Synopse SQLite3 database engine}, whatever its name states, is not bound to {\i SQLite3} (you can use another database engine for data storage, for example we provide a {\f1\fs20 TSQLRestServerStaticInMemory} class which implements a fast but limited in-memory database engine). Therefore, the {\i SQLite3} engine itself is not implemented in the @!TSQLRestServer!Lib\SQLite3\mORMot.pas@ unit, but in dedicated units.
The {\i SQLite3} engine is accessed at two levels:
- A low-level direct access to the {\i SQLite3} library, implemented in @!TSQLRequest.Execute,TSQLDataBase,TSQLTableDB.Create!Lib\SynSQLite3.pas@;
- A high-level access, implementing a Client-Side or Server-Side native {\f1\fs20 TSQLRest} descendant using the {\i SQLite3} library for its data persistence, in @!TSQLRestServerDB,TSQLRestClientDB!Lib\SQLite3\mORMotSQLite3.pas@.
: Low-Level access to the library
:  Compilation of the SQLite3 engine
First of all, the original source code of the library, which is retrieved from the official {\i SQLite3} web site in the form of the optimized Amalgamation file - see @http://www.sqlite.org/amalgamation.html - is compiled using the free Borland C++ command-line compiler.
&Here are the defines used for this compilation:
&//#define SQLITE_ENABLE_FTS3
&//  this unit is FTS3-ready, but not compiled with it by default
&//  if you don't use FTS3, dont define this conditional: you'll spare 50KB of code
................................................................................
Some Delphi classes are introduced to manage all calls and statements to C-language interface to {\i SQLite}, mapping all {\f1\fs20 sqlite3_*} functions and methods to object-oriented methods.
The @!TSQLTableDB,TSQLRequest,TSQLDataBase,TSQLBlobStream,ESQLException!Lib\SynSQLite3.pas@ unit defines the following classes:
- {\f1\fs20 ESQLException} is a custom {\i SQLite3} dedicated Exception type;
- {\f1\fs20 TSQLDataBase} is a simple wrapper for direct {\i SQLite3} database manipulation;
- {\f1\fs20 TSQLRequest} encapsulates a {\i SQLite3} request;
- {\f1\fs20 TSQLTableDB} executes a @*SQL@ statement in the local {\f1\fs20 SQLite3} database engine, and get result in memory, as JSON content;
- {\f1\fs20 TSQLBlobStream} is available to access to a {\i SQLite3} BLOB Stream.
Those database access types are then used by the following Client-Server @*REST@ful classes, to implement {\i SQLite3} storage for persistence of our @*ORM@ (the so called objects hibernation) in @!TSQLRestClientDB,TSQLRestServerDB!Lib\SQLite3\mORMotSQLite3.pas@:
- {\f1\fs20 TSQLRestClientDB} implements a REST client with direct access to a {\i SQLite3} database, that is without the Client-Server aspect of the framework;
- {\f1\fs20 TSQLRestServerDB} can be used to implement a REST server using {\i SQLite3} as its storage engine.

[SDD-DI-2.2.2]
; SRS-DI-2.2.2 - The framework libraries, including all its {\i SQLite3} related features, must be tested using Unitary testing
:Implementation
Some @*test@s classes have been developed, which methods cover most aspect of the framework:
................................................................................
\TTestCompression\TSynTestCase
\TTestClientServerAccess\TSynTestCase
\TTestBigTable\TSynTestCase
\TTestBasicClasses\TSynTestCase
\TSynTestCase\TSynTest
rankdir=LR;
\
Those classes are implemented in @!TTestLowLevelCommon,TTestLowLevelTypes,TTestBasicClasses,TTestSQLite3Engine,TTestFileBased,TTestMemoryBased,TTestFileBasedWAL,TTestClientServerAccess!Lib\SQLite3\SynSelfTests.pas@ units.

[SDD-DI-2.2.3]
; SRS-DI-2.2.3 - The framework shall be able to access any external database, via OleDB or direct access for Oracle (OCI) or SQLite3 (for external database files)
:SynDB classes
The @SAD@ document detailed the architecture, and main implementation part of the database-agnostic features of the framework.
:Faster late binding
For both our {\i SynDB} and {\i SynBigTable} units, we allow {\i late-binding} of data row values, using a variant and direct named access of properties. It's a very convenient way of accessing result rows values.
................................................................................
:SynFile main Demo
The @SAD-SynFile@ section of the associated @SAD@ has already detailed the architecture and the code used to produce a full featured application, including the User Interface generation.
Please refer to these pages for sample code and general explanation about this feature of the framework.

[SDD-DI-2.3.1.1]
; SRS-DI-2.3.1.1 - Database Grid Display, providing data in the Client Application
:Implementation
A standard {\f1\fs20 TDrawGrid} can be associated to a {\f1\fs20 TSQLTable} instance by using a {\f1\fs20 TSQLTableToGrid} object, as defined in the @!TSQLTableToGrid.Create!Lib\SQLite3\mORMotUI.pas@:
- Just call {\f1\fs20 TSQLTableToGrid.Create(Grid,Table)} to initiate the association;
- The Table will be released when no longer necessary;
- Any former association by {\f1\fs20 TSQLTableToGrid.Create()} will be overridden;
- Handle Unicode, auto column size, field sort, incremental key lookup, optional hide ID;
- {\i Ctrl + click} on a cell to display its full Unicode content.
For instance, here is how the @!TSQLLister.Create!Lib\SQLite3\mORMotToolBar.pas@ unit creates a grid for every {\f1\fs20 TSQLRecord} class it refers to:
!constructor TSQLLister.Create(aOwner: TComponent; aClient: TSQLRestClientURI;
!  (...)
!!  fTableToGrid := TSQLTableToGrid.From(fGrid);
!  if fTableToGrid=nil then begin
!    // this Grid has no associated TSQLTableToGrid -> create default one
!    if fClient.InheritsFrom(TSQLRestClientURI) then
!      C := TSQLRestClientURI(fClient) else
................................................................................
:Implementation
: Rendering
The current implementation of the framework User Interface generation handles two kind of rendering:
- Native VCL components;
- Proprietary TMS components.
You can select which set of components are used, by defining - globally to your project (i.e. in the {\i Project/Options/Conditionals} menu) - the {\f1\fs20 USETMSPACK} conditional. If it is not set (which is by default), it will use VCL components.
: Ribbon-like toolbars
As stated by the @SAD-SynFile@ section of the associated @SAD@, ribbon-like toolbars can be generated by using the {\f1\fs20 TSQLRibbon} class, as defined in @!TSQLRibbon.Create,TSQLRibbonTab,TSQLLister,TSQLCustomToolBar.Init!Lib\SQLite3\mORMotToolBar.pas@.
This class will use one {\f1\fs20 TSQLRibbonTab} instance per {\f1\fs20 TSQLRecord} class type it handles, displayed on its own ribbon page, with an associated {\f1\fs20 TDrawGrid} instance and a {\f1\fs20 TGDIPages} report, via a corresponding {\f1\fs20 TSQLLister} instance. Parameters provided from code to the {\f1\fs20 TSQLRibbon. Create} method can customize the toolbar content on purpose. Actions will be provided as an enumeration type, and button captions will be extracted by {\i Un @*Camel@ Casing} of each @*enumerate@d value, using @*RTTI@.
: Stand-alone toolbars
A {\f1\fs20 TSQLCustomToolBar} object can be used to create some generic toolbars, with just some icons and actions on screen, with no reference to any associated {\f1\fs20 TSQLRecord} class. See for instance this sample code:
!procedure TMainLogView.FormCreate(Sender: TObject);
!begin
!  FToolBar.Init(self,TypeInfo(TLogViewAction),ActionClick,ImageList,'');
!  FToolBar.AddToolBar('Test')
!end;
The above lines will create a panel on the owner form, with a toolbar containing one button per each {\f1\fs20 TLogViewAction} element. Icons will be taken from the supplied {\f1\fs20 ImageList} component, and the {\f1\fs20 ActionClick} event handler will be called when a button is pressed.

[SDD-DI-2.3.1.3]
; SRS-DI-2.3.1.3 - Internationalization (i18n) of the whole User Interface
:Implementation
The @!TLanguage,TLanguageFile.Create,S2U,U2S,TLanguageFile.StringToUTF8,TLanguageFile.TimeToText,TLanguageFile.DateToText,TLanguageFile.DateTimeToText,TLanguageFile.UTF8ToString,TLanguageFile.Translate,_!Lib\SQLite3\mORMoti18n.pas@ unit is able to handle both Internationalization (i18n) and Localization (L10n).
The {\f1\fs20 TLanguageFile} class is able to retrieve a custom list of text, and use it for all {\f1\fs20 resourcestring} and screen captions. The global {\f1\fs20 _()} function, or the {\f1\fs20 Translate} method of the {\f1\fs20 TLanguageFile} class can be used to translate any English text into the corresponding language.
The generic {\f1\fs20 string} type is used when some text is to be displayed on screen. Dedicated {\f1\fs20 U2S} and {\f1\fs20 S2U} functions, or even better the {\f1\fs20 UTF8ToString} and {\f1\fs20 StringToUTF8} methods of a {\f1\fs20 TLanguageFile} instance can be used for proper conversion.
Localization is performed via some dedicated methods of the {\f1\fs20 TLanguageFile} class, like {\f1\fs20 DateToText, DateTimeToText, TimeToText}.

[SDD-DI-2.3.2]
; SRS-DI-2.3.2 - A reporting feature, with full preview and export as PDF or TXT files, must be integrated
:Implementation
The @!TGDIPages!Lib\SQLite3\mORMotReport.pas@ unit implements a reporting component named {\f1\fs20 TGDIPages}, with full preview and {\f1\fs20 txt/pdf} export.
Anti-aliased drawing is using the @!TGDIPlus.DrawAntiAliased!Lib\SynGdiPlus.pas@ unit, and the {\f1\fs20 TGDIPlus. DrawAntiAliased} method.
The pdf export itself is implemented via the @!TPdfDocument,TPdfCanvas.RenderMetaFile!Lib\SynPdf.pas@ unit, via a {\f1\fs20 TPdfDocument} component: every page content (in fact, a {\f1\fs20 TMetaFile} instance) is rendered via the {\f1\fs20 TPdfCanvas. RenderMetaFile} method.

[VV]
Owner=SRS
Order=SRS
DisplayName=V&V Plan

Changes to SQLite3/Samples/01 - In Memory ORM/Project01.dpr.

1
2
3
4
5
6
7
8
9
..
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
   Synopse SQLite3 database framework

   Sample 01 - In Memory ORM
     purpose of this sample is to show the basic ORM usage of the framework:

   - a TRecord class is defined in Unit1.pas
   - a static server (i.e. in-memory database) is initialized (see
     TSQLRestServerStatic.Create below); 
................................................................................
}

program Project01;

uses
  Forms,
  SysUtils,
  SQLite3Commons,
  Unit1 in 'Unit1.pas' {Form1},
  SampleData in 'SampleData.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Form1.Caption := ' Sample 01 - In Memory ORM';
  Form1.Database := TSQLRestServerStaticInMemory.Create(TSQLSampleRecord,nil,
    ChangeFileExt(paramstr(0),'.db'));
  Application.Run;
end.

|







 







|













1
2
3
4
5
6
7
8
9
..
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
   Synopse mORMot framework

   Sample 01 - In Memory ORM
     purpose of this sample is to show the basic ORM usage of the framework:

   - a TRecord class is defined in Unit1.pas
   - a static server (i.e. in-memory database) is initialized (see
     TSQLRestServerStatic.Create below); 
................................................................................
}

program Project01;

uses
  Forms,
  SysUtils,
  mORMot,
  Unit1 in 'Unit1.pas' {Form1},
  SampleData in 'SampleData.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Form1.Caption := ' Sample 01 - In Memory ORM';
  Form1.Database := TSQLRestServerStaticInMemory.Create(TSQLSampleRecord,nil,
    ChangeFileExt(paramstr(0),'.db'));
  Application.Run;
end.

Changes to SQLite3/Samples/01 - In Memory ORM/Project01.txt.

1
2
3
4
5
6
7
8
   Synopse SQLite3 database framework

   Sample 01 - In Memory ORM
     purpose of this sample is to show the basic ORM usage of the framework:

   - a TSampleRecord class is defined in Unit1.pas
   - a static server (i.e. in-memory database) is initialized (see
     TSQLRestServerStatic.Create below); 
|







1
2
3
4
5
6
7
8
   Synopse mORMot framework

   Sample 01 - In Memory ORM
     purpose of this sample is to show the basic ORM usage of the framework:

   - a TSampleRecord class is defined in Unit1.pas
   - a static server (i.e. in-memory database) is initialized (see
     TSQLRestServerStatic.Create below); 

Changes to SQLite3/Samples/01 - In Memory ORM/SampleData.pas.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// - this unit will be shared between client and server
unit SampleData;

interface

uses
  SynCommons,
  SQLite3Commons;

type
  /// here we declare the class containing the data
  // - it just has to inherits from TSQLRecord, and the published
  // properties will be used for the ORM (and all SQL creation)
  // - the beginning of the class name must be 'TSQL' for proper table naming
  // in client/server environnment







|







2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// - this unit will be shared between client and server
unit SampleData;

interface

uses
  SynCommons,
  mORMot;

type
  /// here we declare the class containing the data
  // - it just has to inherits from TSQLRecord, and the published
  // properties will be used for the ORM (and all SQL creation)
  // - the beginning of the class name must be 'TSQL' for proper table naming
  // in client/server environnment

Changes to SQLite3/Samples/01 - In Memory ORM/Unit1.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, SQLite3Commons,
  SampleData;

type
  TForm1 = class(TForm)
    QuestionMemo: TMemo;
    NameEdit: TEdit;
    Label1: TLabel;
................................................................................

procedure TForm1.AddButtonClick(Sender: TObject);
var Rec: TSQLSampleRecord;
begin
  Rec := TSQLSampleRecord.Create;
  try
    // we use explicit StringToUTF8() for conversion below
    // a real application should use TLanguageFile.StringToUTF8() in SQLite3i18n
    Rec.Name := StringToUTF8(NameEdit.Text);
    Rec.Question := StringToUTF8(QuestionMemo.Text);
    if Database.Add(Rec,true)=0 then
      ShowMessage('Error adding the data') else begin
      NameEdit.Text := '';
      QuestionMemo.Text := '';
      NameEdit.SetFocus;







|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, mORMot,
  SampleData;

type
  TForm1 = class(TForm)
    QuestionMemo: TMemo;
    NameEdit: TEdit;
    Label1: TLabel;
................................................................................

procedure TForm1.AddButtonClick(Sender: TObject);
var Rec: TSQLSampleRecord;
begin
  Rec := TSQLSampleRecord.Create;
  try
    // we use explicit StringToUTF8() for conversion below
    // a real application should use TLanguageFile.StringToUTF8() in mORMoti18n
    Rec.Name := StringToUTF8(NameEdit.Text);
    Rec.Question := StringToUTF8(QuestionMemo.Text);
    if Database.Add(Rec,true)=0 then
      ShowMessage('Error adding the data') else begin
      NameEdit.Text := '';
      QuestionMemo.Text := '';
      NameEdit.SetFocus;

Changes to SQLite3/Samples/02 - Embedded SQLite3 ORM/Project02.dpr.

1
2
3
4
5
6
7
8
9
..
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
   Synopse SQLite3 database framework

   Sample 02 - Embedded SQLite3 ORM
     purpose of this sample is to show embedded SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - a SQLite3 server is initialized (see TSQLRestServerDB.Create below) and
     will work embedded, i.e. not in Client/Server mode here
................................................................................

}
program Project02;

uses
  Forms,
  SysUtils,
  SQLite3Commons,
  SQLite3,
  Unit1 in '..\01 - In Memory ORM\Unit1.pas' {Form1},
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

{$R *.res}

begin
  Application.Initialize;

|







 







|
|







1
2
3
4
5
6
7
8
9
..
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
   Synopse mORMot framework

   Sample 02 - Embedded SQLite3 ORM
     purpose of this sample is to show embedded SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - a SQLite3 server is initialized (see TSQLRestServerDB.Create below) and
     will work embedded, i.e. not in Client/Server mode here
................................................................................

}
program Project02;

uses
  Forms,
  SysUtils,
  mORMot,
  mORMotSQLite3,
  Unit1 in '..\01 - In Memory ORM\Unit1.pas' {Form1},
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

{$R *.res}

begin
  Application.Initialize;

Changes to SQLite3/Samples/02 - Embedded SQLite3 ORM/Project02.txt.

1
2
3
4
5
6
7
8
   Synopse SQLite3 database framework

   Sample 02 - Embedded SQLite3 ORM
     purpose of this sample is to show embedded SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - a SQLite3 server is initialized (see TSQLRestServerDB.Create below) and
     will work embedded, i.e. not in Client/Server mode here
|







1
2
3
4
5
6
7
8
   Synopse mORMot framework

   Sample 02 - Embedded SQLite3 ORM
     purpose of this sample is to show embedded SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - a SQLite3 server is initialized (see TSQLRestServerDB.Create below) and
     will work embedded, i.e. not in Client/Server mode here

Changes to SQLite3/Samples/03 - NamedPipe Client-Server/Project03.txt.

1
2
3
4
5
6
7
8
..
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   Synopse SQLite3 database framework

   Sample 03 - NamedPipe Client-Server
     purpose of this sample is to show Client/Server SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - this sample uses down projects, Project03Client.dpr and Project03Server.dpr
   - a SQLite3 server is initialized in Project03Server
................................................................................
     SQLite3 database
   - one or more client instances can be run in Project03Client
   - the purpose of the Client form in Unit1.pas is to add a record to the
     database; the Time field is filled with the current date and time
   - the 'Find a previous message' button show how to perform a basic query
   - since the framework use UTF-8 encoding, we use some basic functions for
     fast conversion to/from the User Interface; in real applications,
     you should better use our SQLite3i18n unit and the corresponding
     TLanguageFile.StringToUTF8() and TLanguageFile.UTF8ToString() methods
   - note that you didn't need to write any SQL statement, only define a
     class and call some methods; even the query was made very easy (just an
     obvious WHERE clause to write)
   - thanks to the true object oriented modeling of the framework, the same
     exact Unit1 is used for both static in-memory database engine, or
     with SQLite3 database storage: only the TForm1.Database object creation
     instance was modified
   - look at the tiny size of the EXE (even with SQLite3 engine embedded), less
     than 400KB for the server, and 80KB for the client, with LVCL :)


  Version 1.0 - January 24, 2010
|







 







|













1
2
3
4
5
6
7
8
..
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   Synopse mORMot framework

   Sample 03 - NamedPipe Client-Server
     purpose of this sample is to show Client/Server SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - this sample uses down projects, Project03Client.dpr and Project03Server.dpr
   - a SQLite3 server is initialized in Project03Server
................................................................................
     SQLite3 database
   - one or more client instances can be run in Project03Client
   - the purpose of the Client form in Unit1.pas is to add a record to the
     database; the Time field is filled with the current date and time
   - the 'Find a previous message' button show how to perform a basic query
   - since the framework use UTF-8 encoding, we use some basic functions for
     fast conversion to/from the User Interface; in real applications,
     you should better use our mORMoti18n unit and the corresponding
     TLanguageFile.StringToUTF8() and TLanguageFile.UTF8ToString() methods
   - note that you didn't need to write any SQL statement, only define a
     class and call some methods; even the query was made very easy (just an
     obvious WHERE clause to write)
   - thanks to the true object oriented modeling of the framework, the same
     exact Unit1 is used for both static in-memory database engine, or
     with SQLite3 database storage: only the TForm1.Database object creation
     instance was modified
   - look at the tiny size of the EXE (even with SQLite3 engine embedded), less
     than 400KB for the server, and 80KB for the client, with LVCL :)


  Version 1.0 - January 24, 2010

Changes to SQLite3/Samples/03 - NamedPipe Client-Server/Project03Client.dpr.

1
2
3
4
5
6
7
8
9
..
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
   Synopse SQLite3 database framework

   Sample 03 - NamedPipe Client-Server
     purpose of this sample is to show Client/Server SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - this sample uses down projects, Project03Client.dpr and Project03Server.dpr
   - a SQLite3 server is initialized in Project03Server
................................................................................
     SQLite3 database
   - one or more client instances can be run in Project03Client
   - the purpose of the Client form in Unit1.pas is to add a record to the
     database; the Time field is filled with the current date and time
   - the 'Find a previous message' button show how to perform a basic query
   - since the framework use UTF-8 encoding, we use some basic functions for
     fast conversion to/from the User Interface; in real applications,
     you should better use our SQLite3i18n unit and the corresponding
     TLanguageFile.StringToUTF8() and TLanguageFile.UTF8ToString() methods
   - note that you didn't need to write any SQL statement, only define a
     class and call some methods; even the query was made very easy (just an
     obvious WHERE clause to write)
   - thanks to the true object oriented modeling of the framework, the same
     exact Unit1 is used for both static in-memory database engine, or
     with SQLite3 database storage: only the TForm1.Database object creation
................................................................................
  Version 1.0 - January 24, 2010
}
program Project03Client;

uses
  Forms,
  SysUtils,
  SQLite3Commons,
  Unit1 in '..\01 - In Memory ORM\Unit1.pas' {Form1},
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Form1.Caption := ' Sample 03 - NamedPipe Client';
  Form1.Database := TSQLRestClientURINamedPipe.Create(Form1.Model,'03');
  Application.Run;
end.

|







 







|







 







|












1
2
3
4
5
6
7
8
9
..
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
   Synopse mORMot framework

   Sample 03 - NamedPipe Client-Server
     purpose of this sample is to show Client/Server SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - this sample uses down projects, Project03Client.dpr and Project03Server.dpr
   - a SQLite3 server is initialized in Project03Server
................................................................................
     SQLite3 database
   - one or more client instances can be run in Project03Client
   - the purpose of the Client form in Unit1.pas is to add a record to the
     database; the Time field is filled with the current date and time
   - the 'Find a previous message' button show how to perform a basic query
   - since the framework use UTF-8 encoding, we use some basic functions for
     fast conversion to/from the User Interface; in real applications,
     you should better use our mORMoti18n unit and the corresponding
     TLanguageFile.StringToUTF8() and TLanguageFile.UTF8ToString() methods
   - note that you didn't need to write any SQL statement, only define a
     class and call some methods; even the query was made very easy (just an
     obvious WHERE clause to write)
   - thanks to the true object oriented modeling of the framework, the same
     exact Unit1 is used for both static in-memory database engine, or
     with SQLite3 database storage: only the TForm1.Database object creation
................................................................................
  Version 1.0 - January 24, 2010
}
program Project03Client;

uses
  Forms,
  SysUtils,
  mORMot,
  Unit1 in '..\01 - In Memory ORM\Unit1.pas' {Form1},
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Form1.Caption := ' Sample 03 - NamedPipe Client';
  Form1.Database := TSQLRestClientURINamedPipe.Create(Form1.Model,'03');
  Application.Run;
end.

Changes to SQLite3/Samples/03 - NamedPipe Client-Server/Project03Server.dpr.

1
2
3
4
5
6
7
8
9
{
   Synopse SQLite3 database framework

   Sample 03 - NamedPipe Client-Server
     purpose of this sample is to show Client/Server SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - this sample uses down projects, Project03Client.dpr and Project03Server.dpr
   - a SQLite3 server is initialized in Project03Server

|







1
2
3
4
5
6
7
8
9
{
   Synopse mORMot framework

   Sample 03 - NamedPipe Client-Server
     purpose of this sample is to show Client/Server SQLite3 database usage:

   - a TSampleRecord class is defined in Unit1.pas
   - this sample uses down projects, Project03Client.dpr and Project03Server.dpr
   - a SQLite3 server is initialized in Project03Server

Changes to SQLite3/Samples/03 - NamedPipe Client-Server/Unit2.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, SQLite3Commons, SQLite3, StdCtrls, SampleData;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, mORMot, mORMotSQLite3, StdCtrls, SampleData;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);

Changes to SQLite3/Samples/04 - HTTP Client-Server/Project04Client.dpr.

1
2
3
4
5
6
7
8
9
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
   Synopse SQLite3 database framework

   Sample 04 - HTTP Client-Server
     purpose of this sample is to show HTTP Client/Server SQLite3 database usage:

   - a TSQLSampleRecord class is defined in shared unit SampleData.pas
   - this sample uses two projects, Project04Client.dpr and Project04Server.dpr
   - a SQLite3 server is initialized in Project04Server
................................................................................

program Project04Client;

uses
  Windows,
  Forms,
  SysUtils,
  SQLite3Commons,
  SQLite3HttpClient,
  Unit1 in '..\01 - In Memory ORM\Unit1.pas' {Form1},
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

{$R *.res}

var Server: AnsiString;
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Form1.Caption := ' Sample 04 - HTTP Client';
  if ParamCount=0 then
    Server := 'localhost' else
    Server := AnsiString(Paramstr(1));
  Form1.Database := TSQLite3HttpClient.Create(Server,'8080',Form1.Model);
  TSQLite3HttpClient(Form1.Database).SetUser('User','synopse');
  Application.Run;
end.

|







 







|
|













|
|


1
2
3
4
5
6
7
8
9
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
   Synopse mORMot framework

   Sample 04 - HTTP Client-Server
     purpose of this sample is to show HTTP Client/Server SQLite3 database usage:

   - a TSQLSampleRecord class is defined in shared unit SampleData.pas
   - this sample uses two projects, Project04Client.dpr and Project04Server.dpr
   - a SQLite3 server is initialized in Project04Server
................................................................................

program Project04Client;

uses
  Windows,
  Forms,
  SysUtils,
  mORMot,
  mORMotHttpClient,
  Unit1 in '..\01 - In Memory ORM\Unit1.pas' {Form1},
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

{$R *.res}

var Server: AnsiString;
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Form1.Caption := ' Sample 04 - HTTP Client';
  if ParamCount=0 then
    Server := 'localhost' else
    Server := AnsiString(Paramstr(1));
  Form1.Database := TSQLHttpClient.Create(Server,'8080',Form1.Model);
  TSQLHttpClient(Form1.Database).SetUser('User','synopse');
  Application.Run;
end.

Changes to SQLite3/Samples/04 - HTTP Client-Server/Project04Server.dpr.

1
2
3
4
5
6
7
8
9
{
   Synopse SQLite3 database framework

   Sample 04 - HTTP Client-Server
     purpose of this sample is to show HTTP Client/Server SQLite3 database usage:

   - a TSQLSampleRecord class is defined in shared unit SampleData.pas
   - this sample uses two projects, Project04Client.dpr and Project04Server.dpr
   - a SQLite3 server is initialized in Project04Server

|







1
2
3
4
5
6
7
8
9
{
   Synopse mORMot framework

   Sample 04 - HTTP Client-Server
     purpose of this sample is to show HTTP Client/Server SQLite3 database usage:

   - a TSQLSampleRecord class is defined in shared unit SampleData.pas
   - this sample uses two projects, Project04Client.dpr and Project04Server.dpr
   - a SQLite3 server is initialized in Project04Server

Changes to SQLite3/Samples/04 - HTTP Client-Server/Unit2.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, SQLite3Commons, SQLite3, SQLite3HttpServer, SampleData;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
................................................................................
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
  public
    Model: TSQLModel;
    DB: TSQLRestServerDB;
    Server: TSQLite3HttpServer;
  end;

var
  Form1: TForm1;

implementation

................................................................................
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Model := CreateSampleModel;
  DB := TSQLRestServerDB.Create(Model,ChangeFileExt(paramstr(0),'.db3'),true);
  DB.CreateMissingTables(0);
  Server := TSQLite3HttpServer.Create('8080',[DB]);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Server.Free;
  DB.Free;
  Model.Free;







|







 







|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, mORMot, mORMotSQLite3, mORMotHttpServer, SampleData;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
................................................................................
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
  public
    Model: TSQLModel;
    DB: TSQLRestServerDB;
    Server: TSQLHttpServer;
  end;

var
  Form1: TForm1;

implementation

................................................................................
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Model := CreateSampleModel;
  DB := TSQLRestServerDB.Create(Model,ChangeFileExt(paramstr(0),'.db3'),true);
  DB.CreateMissingTables(0);
  Server := TSQLHttpServer.Create('8080',[DB]);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Server.Free;
  DB.Free;
  Model.Free;

Changes to SQLite3/Samples/05 - Report created from code/Unit1.dfm.

1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
..
69
70
71
72
73
74
75








76

object Form1: TForm1
  Left = 255
  Top = 208
  Width = 337
  Height = 355
  Caption = ' SQLite3Pages Test'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False

  PixelsPerInch = 96
  TextHeight = 13
  object lbl1: TLabel
    Left = 32
    Top = 16
    Width = 62
    Height = 13
................................................................................
    Top = 264
    Width = 113
    Height = 33
    Caption = 'Quit'
    TabOrder = 3
    OnClick = btn2Click
  end








end


|
|
|









>







 







>
>
>
>
>
>
>
>
|
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
object Form1: TForm1
  Left = 258
  Top = 211
  Width = 321
  Height = 355
  Caption = ' SQLite3Pages Test'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnShow = FormShow
  PixelsPerInch = 96
  TextHeight = 13
  object lbl1: TLabel
    Left = 32
    Top = 16
    Width = 62
    Height = 13
................................................................................
    Top = 264
    Width = 113
    Height = 33
    Caption = 'Quit'
    TabOrder = 3
    OnClick = btn2Click
  end
  object Button1: TButton
    Left = 208
    Top = 56
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 4
    OnClick = Button1Click
  end
end

Changes to SQLite3/Samples/05 - Report created from code/Unit1.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

18
19


20
21
22
23
24
25
26
..
32
33
34
35
36
37
38



39
40
41

42




43
44
45
46
47
48
49
50
51
52

53
54













55
56

57
58
59
60
61
62
63
64
65
66
67
68
69













70
71
72

73

74
75
76
77
78
79
80
81
..
85
86
87
88
89
90
91

92





93
94
95
96


97
98

99
100
101
102
103
104
105
106





























































































































107

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SQLite3Pages;

type
  TForm1 = class(TForm)
    edt1: TEdit;
    lbl1: TLabel;
    Label1: TLabel;
    mmo1: TMemo;
    btn1: TButton;
    btn2: TButton;

    procedure btn2Click(Sender: TObject);
    procedure btn1Click(Sender: TObject);


  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
................................................................................
{$R Vista.res}

procedure TForm1.btn2Click(Sender: TObject);
begin
  Close;
end;




procedure TForm1.btn1Click(Sender: TObject);
var i: integer;
    Bmp: TBitmap;

    s: string;




begin
  Bmp := TBitmap.Create;
  try
    Bmp.Width := ClientWidth;
    Bmp.Height := ClientHeight;
    PaintTo(Bmp.Canvas,0,0); // create some bitmap content
    with TGDIPages.Create(self) do
    try
      // the name of the report is taken from main Window's caption
      Caption := self.Caption;

      // now we add some content to the report
      BeginDoc;













      // header and footer
      Font.Name := 'Georgia';

      Font.Size := 11;
      SaveLayout;
      Font.Style := [fsItalic,fsUnderline];
      TextAlign := taRight;
      AddTextToHeaderAt('http://synopse.info',RightMarginPos);
      Font.Style := [];
      AddLineToFooter(false);
      AddPagesToFooterAt('Page %d/%d',RightMarginPos);
      RestoreSavedLayout;
      AddTextToHeader(ExtractFileName(paramstr(0)));
      AddTextToFooter(DateTimeToStr(Now));
      AddLineToHeader(false);
      Font.Size := 12;













      // main content (automaticaly split on next pages)
      NewHalfLine;
      TextAlign := taJustified;

      s := 'This is some big text which must be justified on multiple lines. ';

      DrawText(s+s+s+s);
      NewLine;
      TextAlign := taLeft;
      DrawTitle(edt1.Text,true);
      for i := 1 to 10 do
        DrawText('This is some text '+IntToStr(i));
      NewLine;
      DrawBMP(Bmp,maxInt,50,'Some bitmap in the report');
................................................................................
      AddColumnHeaders(['#','Two','Three'],true,true);
      for i := 1 to 100 do
        DrawTextAcrossCols([IntToStr(i),'Column '+IntToStr(i),'Some text here. '+s]);
      NewLine;
      DrawBMP(Bmp,maxInt,50,'Some bitmap in the report (twice)');
      DrawTitle('This is your text',false,0,'','bookmarkname');
      DrawText(mmo1.Text);

      EndDoc;





      // set optional PDF export options
      // ExportPDFForceJPEGCompression := 80;
      // ExportPDFEmbeddedTTF := true;
      // ExportPDFUseUniscribe := true;


      // show a preview form, and allow basic actions via the right click menu
      ShowPreviewForm;

    finally
      Free;
    end;
  finally
    Bmp.Free;
  end;
end;






























































































































end.







|
|









>


>
>







 







>
>
>

<
|
>

>
>
>
>










>


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


>







|





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



>
|
>
|







 







>

>
>
>
>
>




>
>
|

>








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

>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
..
35
36
37
38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ShellApi, Printers,
  SynCommons, SynPdf, mORMotReport;

type
  TForm1 = class(TForm)
    edt1: TEdit;
    lbl1: TLabel;
    Label1: TLabel;
    mmo1: TMemo;
    btn1: TButton;
    btn2: TButton;
    Button1: TButton;
    procedure btn2Click(Sender: TObject);
    procedure btn1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
................................................................................
{$R Vista.res}

procedure TForm1.btn2Click(Sender: TObject);
begin
  Close;
end;

const
  UNICODE: array[0..5] of WideChar =
   (#27161,#28310,#33836,#22283,#30908,#0);
procedure TForm1.btn1Click(Sender: TObject);

var Bmp: TBitmap;
    U: RawUTF8;
    s: string;
    i: integer;
{   R, Marg: TRect;
    iz: TSize;
    M: TMetaFile;}
begin
  Bmp := TBitmap.Create;
  try
    Bmp.Width := ClientWidth;
    Bmp.Height := ClientHeight;
    PaintTo(Bmp.Canvas,0,0); // create some bitmap content
    with TGDIPages.Create(self) do
    try
      // the name of the report is taken from main Window's caption
      Caption := self.Caption;
      //Orientation := poLandscape;
      // now we add some content to the report
      BeginDoc;
      {
      M := TMetaFile.Create;
      M.LoadFromFile('emf1.emf');
      Siz := PaperSize;
      Marg := PageMargins;
      R.Left := Marg.Left;
      R.Right := Siz.cx-Marg.Right;
      R.Top := Marg.Top;
      R.Bottom := Siz.cy-Marg.Top;
      DrawMeta(R,M);
      M.Free;
      }

      // header and footer
      Font.Name := 'Georgia';
      //Font.Name := 'Arial Unicode MS';
      Font.Size := 11;
      SaveLayout;
      Font.Style := [fsItalic,fsUnderline];
      TextAlign := taRight;
      AddTextToHeaderAt('http://synopse.info',RightMarginPos);
      Font.Style := [];
      AddLineToFooter(false);
      AddPagesToFooterAt(sPageN,RightMarginPos);
      RestoreSavedLayout;
      AddTextToHeader(ExtractFileName(paramstr(0)));
      AddTextToFooter(DateTimeToStr(Now));
      AddLineToHeader(false);
      Font.Size := 12;
      ExportPDFForceJPEGCompression := 0;

{      // test
      WordWrapLeftCols := true;
      AddColumns([10,22,22,22,22]);
      AddColumnHeaders(['#','Two','Three','4','5'],true,true);
      for i := 1 to 50 do
        DrawTextAcrossCols([IntToStr(i),'Column '+IntToStr(i),
        'This is some big text which must be justified on multiple lines. Text "four" and "five" will be invisible in pdf...',
        'four','five']);
      EndDoc;
      ExportPDF('cells.pdf',True,True);}

      // main content (automaticaly split on next pages)
      NewHalfLine;
      TextAlign := taJustified;
      U := RawUnicodeToUtf8(UNICODE,StrLenW(UNICODE));
      U := 'This is some big '+U+' text which must be justified on multiple lines. ';
      U := U+U+U+U;
      DrawTextU(U);
      NewLine;
      TextAlign := taLeft;
      DrawTitle(edt1.Text,true);
      for i := 1 to 10 do
        DrawText('This is some text '+IntToStr(i));
      NewLine;
      DrawBMP(Bmp,maxInt,50,'Some bitmap in the report');
................................................................................
      AddColumnHeaders(['#','Two','Three'],true,true);
      for i := 1 to 100 do
        DrawTextAcrossCols([IntToStr(i),'Column '+IntToStr(i),'Some text here. '+s]);
      NewLine;
      DrawBMP(Bmp,maxInt,50,'Some bitmap in the report (twice)');
      DrawTitle('This is your text',false,0,'','bookmarkname');
      DrawText(mmo1.Text);

      EndDoc;
      ForceInternalAntiAliasedFontFallBack := true;
      ForceNoAntiAliased := true;
      //ForceInternalAntiAliased := false;
      ExportPDFAuthor := 'A.Bouchez';
      ExportPDFSubject := 'This is some sample file';
      // set optional PDF export options
      // ExportPDFForceJPEGCompression := 80;
      // ExportPDFEmbeddedTTF := true;
      // ExportPDFUseUniscribe := true;
      // ExportPDFA1 := true;
      ExportPDF('test.pdf',true,true); close; exit;
      // show a preview form, and allow basic actions via corresponding buttons
      ShowPreviewForm;
  
    finally
      Free;
    end;
  finally
    Bmp.Free;
  end;
end;


procedure TForm1.FormShow(Sender: TObject);
var FN: TFileName;
    M: TMetaFile;
    i: integer;
begin
  exit;
  //btn1Click(nil); Close; exit;
  //Button1Click(nil); Close; exit;
  with TPdfDocument.Create do
  try
    for i := 0 to 24 do begin
      AddPage;
      M := TMetaFile.Create;
      M.LoadFromFile(IntToStr(i)+'.emf');
      Canvas.RenderMetaFile(M,Canvas.Page.PageHeight/M.Height*1.3);
      M.Free;
    end;
{    AddPage;
    with Canvas do
    begin
      SetFont('Arial',12,[fsBold]);
      TextOut(100,500,'Test');
      MoveTo(100,400);
      LineTo(500,500);
      Stroke;
    end; }
    FN := ChangeFileExt(paramstr(0),'.pdf');
    SaveToFile(FN);
    ShellExecute(Handle,nil,pointer(FN),nil,nil,SW_SHOWNORMAL);
  finally
    Free;
  end;
  Close;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i, y: integer;
  TestImage: TBitmap;
  MF: TMetaFile;
  R: TRect;
begin
  with TGDIPages.Create(self) do
  try
    BeginDoc;
    MF := TMetafile.Create;
    MF.LoadFromFile('d:\download\emf1.emf');
    DrawGraphic(MF,0,PaperSize.cx-20);
{    for y := 0 to 4 do
    begin

      DrawTitle(edt1.Text, true);
      for i := 1 to 10 do
        DrawText('This is some text ' + IntToStr(i));
      NewLine;

      TestImage := TBitmap.Create;
      try
        TestImage.Width := 500;
        TestImage.Height := 500;

        TestImage.Canvas.Pen.Color := clRed;
        TestImage.Canvas.MoveTo(0, y * 80);
        TestImage.Canvas.LineTo(TestImage.Width, y * 80);

        DrawBMP(TestImage, maxInt, RightMarginPos);
      finally
        TestImage.Free;
      end;

      NewPage;
    end;}

    EndDoc;
    ShowPreviewForm;
    //ExportPDF('test.pdf', true, true);
  finally
    Free;
  end;
end;
{
var
  xRect: TRect;
const
  Text: WideString = 'RERERERE:';
begin
  with TPdfDocumentGDI.Create do
  try
    PDFA1 := true;
    AddPage;
    UseUniScribe := false; //uniscribe does not change anything about the problem
    with VCLCanvas do begin
      Font.Name := 'Tahoma';
      Font.Size := 8;
      Font.Style := [fsBold];
      Pen.Color := $AAAAAA;

      xRect := Rect(0, 0, TextWidth(Text), TextHeight(Text));
      OffsetRect(xRect, 100, 100);

      Rectangle(xRect);

      Windows.ExtTextOutW(Handle, xRect.Left, xRect.Top, ETO_CLIPPED,
        @xRect, PWideChar(Text), Length(Text), nil);

      Font.Size := 24;

      xRect := Rect(0, 0, TextWidth(Text), TextHeight(Text));
      OffsetRect(xRect, 100, 200);

      Rectangle(xRect);

      Windows.ExtTextOutW(Handle, xRect.Left, xRect.Top, ETO_CLIPPED,
        @xRect, PWideChar(Text), Length(Text), nil);

    end;
    SaveToFile('TestVcl.pdf');
    ShellExecute(Handle,nil,'TestVcl.pdf',nil,nil,SW_SHOWNORMAL);
  finally
    Free;
  end;
end;
}

end.

Changes to SQLite3/Samples/06 - Remote JSON REST Service/Project06ClientMain.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unit Project06ClientMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, SQLite3Commons;

type
  TForm1 = class(TForm)
    edtA: TEdit;
    edtB: TEdit;
    lblA: TLabel;
    lblB: TLabel;







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unit Project06ClientMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, mORMot;

type
  TForm1 = class(TForm)
    edtA: TEdit;
    edtB: TEdit;
    lblA: TLabel;
    lblB: TLabel;

Changes to SQLite3/Samples/06 - Remote JSON REST Service/Project06Server.dpr.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
program Project06Server;

{$APPTYPE CONSOLE}

uses
  SynCommons,
  SQLite3Commons,
  SysUtils;

type
  // TSQLRestServerFullMemory kind of server is light and enough for our purpose
  TServiceServer = class(TSQLRestServerFullMemory)
  published
    function Sum(var aParams: TSQLRestServerCallBackParams): Integer;






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
program Project06Server;

{$APPTYPE CONSOLE}

uses
  SynCommons,
  mORMot,
  SysUtils;

type
  // TSQLRestServerFullMemory kind of server is light and enough for our purpose
  TServiceServer = class(TSQLRestServerFullMemory)
  published
    function Sum(var aParams: TSQLRestServerCallBackParams): Integer;

Changes to SQLite3/Samples/08 - TaskDialog/TaskDialogTest.dpr.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
56
57
58
59
60
61
62























63
64
65
66




67
68
69
70
71
72
73
program TaskDialogTest;

uses
  SysUtils,
  SynTaskDialog,
  SQLite3Commons,
  SQLite3UILogin,
  Forms;

{$R *.res}

{$R Vista.res} // to enable XP/Vista/Seven theming

procedure Test;
................................................................................
  if Task.VerifyChecked then
    ShowMessage(Task.Verify);
  ShowMessage(Format('User=%s Password=%s',[aUserName,aPassword]),
    not TLoginForm.Login('Title','Please login',aUserName,aPassWord,true,''));
  ShowMessage(Format('User=%s Password=%s',[aUserName,aPassword]),
    not TLoginForm.Login('Title','Please login again',aUserName,aPassWord,true,''));
end;
























begin
  Application.Initialize;
  Application.Run;




  Test;
  if @TaskDialogIndirect<>nil then begin
    ShowMessage('Now displaying the dialogs using Delphi emulation');
    @TaskDialogIndirect := nil;
    Test;
  end;
end.





|
|







 







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




>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
program TaskDialogTest;

uses
  SysUtils,
  SynTaskDialog,
  mORMot,
  mORMotUILogin,
  Forms;

{$R *.res}

{$R Vista.res} // to enable XP/Vista/Seven theming

procedure Test;
................................................................................
  if Task.VerifyChecked then
    ShowMessage(Task.Verify);
  ShowMessage(Format('User=%s Password=%s',[aUserName,aPassword]),
    not TLoginForm.Login('Title','Please login',aUserName,aPassWord,true,''));
  ShowMessage(Format('User=%s Password=%s',[aUserName,aPassword]),
    not TLoginForm.Login('Title','Please login again',aUserName,aPassWord,true,''));
end;

procedure Test2;
var
  vDialogue : TTaskDialog;
begin
  vDialogue.Title   := 'My Title';
  vDialogue.Inst    := 'Lorem ipsum dolor sit amet consectetuer';
  vDialogue.Content := 'Libero interdum "' +
                       'necVestibulumidsedetwisinequetinciduntMorbiAliquampedetinciduntSedsempercursusorciipsumipsumegestasProinTortortempus' +
                       '" (' +
                       'neque libero Curabitur Donec non Morbi et odio ' +
                       'Praesent. Felis tincidunt vitae turpis malesuada fames\n\n'+
                       'sodales ac Suspendisse augue Aenean. Euismod Aenean non\n\n' +
                       'Morbi et vitae at hendrerit Quisque vitae accumsan. Tellus pretium adipiscing leo Curabitur\n\n' +
                       'Pellentesque turpis lacus Nulla.\n\n' +
                       'Curabitur faucibus risus eget nisl Lorem libero augue dui Nullam urna. Convallis';
  vDialogue.Buttons := 'Button1'#10'Button2';

  vDialogue.Verify        := 'Ne plus afficher ce message';
  vDialogue.VerifyChecked := False;

  vDialogue.Execute([], 100, [tdfUseCommandLinks], tiWarning);
end;

begin
  Application.Initialize;
  Application.Run;
  Test2;
  @TaskDialogIndirect := nil;
  Test2;
  Exit;
  Test;
  if @TaskDialogIndirect<>nil then begin
    ShowMessage('Now displaying the dialogs using Delphi emulation');
    @TaskDialogIndirect := nil;
    Test;
  end;
end.

Changes to SQLite3/Samples/10 - Background Http service/httpservice.dpr.

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
uses
  {$I SynDprUses.inc}
  Windows,
  Classes,
  SysUtils,
  WinSvc,
  SynCommons,
  SQLite3Service,
  SQLite3Commons,
  SQLite3,
  SQLite3HTTPServer,
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

// define this conditional if you want the GDI messages to be accessible
// from the background service
{.$define USEMESSAGES}


................................................................................
  TSQLite3HttpService = class(TService)
  public
    /// the associated database model
    Model: TSQLModel;
    /// the associated DB
    DB: TSQLRestServerDB;
    /// the background Server processing all requests
    Server: TSQLite3HttpServer;

    /// event trigerred to start the service
    // - e.g. create the Server instance
    procedure DoStart(Sender: TService);
    /// event trigerred to stop the service
    // - e.g. destroy the Server instance
    procedure DoStop(Sender: TService);
................................................................................
procedure TSQLite3HttpService.DoStart(Sender: TService);
begin
  if Server<>nil then
    DoStop(nil); // should never happen
  Model := CreateSampleModel;
  DB := TSQLRestServerDB.Create(Model,ChangeFileExt(paramstr(0),'.db3'));
  DB.CreateMissingTables(0);
  Server := TSQLite3HttpServer.Create('8080',[DB]);
  TSQLLog.Family.Level := LOG_VERBOSE;
  TSQLLog.Add.Log(sllInfo,'Server % started by %',[Server.HttpServer,Server]);
end;

procedure TSQLite3HttpService.DoStop(Sender: TService);
begin
  if Server=nil then







|
|
|
|







 







|







 







|







4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
uses
  {$I SynDprUses.inc}
  Windows,
  Classes,
  SysUtils,
  WinSvc,
  SynCommons,
  mORMotService,
  mORMot,
  mORMotSQLite3,
  mORMotHTTPServer,
  SampleData in '..\01 - In Memory ORM\SampleData.pas';

// define this conditional if you want the GDI messages to be accessible
// from the background service
{.$define USEMESSAGES}


................................................................................
  TSQLite3HttpService = class(TService)
  public
    /// the associated database model
    Model: TSQLModel;
    /// the associated DB
    DB: TSQLRestServerDB;
    /// the background Server processing all requests
    Server: TSQLHttpServer;

    /// event trigerred to start the service
    // - e.g. create the Server instance
    procedure DoStart(Sender: TService);
    /// event trigerred to stop the service
    // - e.g. destroy the Server instance
    procedure DoStop(Sender: TService);
................................................................................
procedure TSQLite3HttpService.DoStart(Sender: TService);
begin
  if Server<>nil then
    DoStop(nil); // should never happen
  Model := CreateSampleModel;
  DB := TSQLRestServerDB.Create(Model,ChangeFileExt(paramstr(0),'.db3'));
  DB.CreateMissingTables(0);
  Server := TSQLHttpServer.Create('8080',[DB]);
  TSQLLog.Family.Level := LOG_VERBOSE;
  TSQLLog.Add.Log(sllInfo,'Server % started by %',[Server.HttpServer,Server]);
end;

procedure TSQLite3HttpService.DoStop(Sender: TService);
begin
  if Server=nil then

Changes to SQLite3/Samples/11 - Exception logging/LoggingTest.dpr.

1
2
3
4
5
6
7
8
9
..
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{
  Synopse SQLite3 database framework

  Sample 11 - Exception logging
    purpose of this sample is to show basic logging mechanism of the framework

  TO HAVE LINE NUMBERS IN THE LOG FILE:
  - Go to Project/Options then set the Linker/File map setting to "Detailed"

................................................................................
program LoggingTest;

uses
  Windows,
  SysUtils,
  //SynZip,
{$ifdef CONDITIONALEXPRESSIONS}
  SQLite3Commons,
  SynSelfTests,
{$endif}
  SynCommons;

type
  /// a class just to show how methods are handled
  TTestLogClass = class
................................................................................
  end;

  /// a custom exception used to show how Delphi exception are handled and
  // can be ignored on request
  ECustomException = class(Exception);

{$ifndef CONDITIONALEXPRESSIONS}
  // SQLite3Commons doesn't compile under Delphi 5 (yet)
  TSQLLog = TSynLog;
{$endif}

var
  TestLevel: TSynLogInfo = high(TSynLogInfo);

const
................................................................................
  ILog.Log(sllDebug,GarbageCollector);
end;


procedure TestsLog;

{$ifdef CONDITIONALEXPRESSIONS}
  // SQLite3Commons doesn't compile under Delphi 5 (yet)
  procedure TestPeopleProc;
  var People: TSQLRecordPeople;
      Log: ISynLog;
  begin
    Log := TSQLLog.Enter;
    People := TSQLRecordPeople.Create;
    try

|







 







|







 







|







 







|







1
2
3
4
5
6
7
8
9
..
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{
  Synopse mORMot framework

  Sample 11 - Exception logging
    purpose of this sample is to show basic logging mechanism of the framework

  TO HAVE LINE NUMBERS IN THE LOG FILE:
  - Go to Project/Options then set the Linker/File map setting to "Detailed"

................................................................................
program LoggingTest;

uses
  Windows,
  SysUtils,
  //SynZip,
{$ifdef CONDITIONALEXPRESSIONS}
  mORMot,
  SynSelfTests,
{$endif}
  SynCommons;

type
  /// a class just to show how methods are handled
  TTestLogClass = class
................................................................................
  end;

  /// a custom exception used to show how Delphi exception are handled and
  // can be ignored on request
  ECustomException = class(Exception);

{$ifndef CONDITIONALEXPRESSIONS}
  // mORMot.pas doesn't compile under Delphi 5 (yet)
  TSQLLog = TSynLog;
{$endif}

var
  TestLevel: TSynLogInfo = high(TSynLogInfo);

const
................................................................................
  ILog.Log(sllDebug,GarbageCollector);
end;


procedure TestsLog;

{$ifdef CONDITIONALEXPRESSIONS}
  // mORMot.pas doesn't compile under Delphi 5 (yet)
  procedure TestPeopleProc;
  var People: TSQLRecordPeople;
      Log: ISynLog;
  begin
    Log := TSQLLog.Enter;
    People := TSQLRecordPeople.Create;
    try

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unit SynDBExplorerClasses;

interface

uses
  SysUtils, Classes,
  SynCommons, SQLite3Commons;

type
  TExpConnectionType = (
    ctOracleDirectOCI, ctOracleOLEDB, ctOracleMSOLEDB, ctMSSQL, ctGenericOLEDB,
    ctSqlite3, ctJet_mdbOLEDB, ctODBC);

  TSQLConnection = class(TSQLRecord)






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
unit SynDBExplorerClasses;

interface

uses
  SysUtils, Classes,
  SynCommons, mORMot;

type
  TExpConnectionType = (
    ctOracleDirectOCI, ctOracleOLEDB, ctOracleMSOLEDB, ctMSSQL, ctGenericOLEDB,
    ctSqlite3, ctJet_mdbOLEDB, ctODBC);

  TSQLConnection = class(TSQLRecord)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unit SynDBExplorerExportTables;

interface

uses
  Windows, Messages, SysUtils, CheckLst, Controls, StdCtrls, Classes, Graphics,
  Forms, ExtCtrls, SQLite3UILogin,
  SynZip, SynCommons, SynDB, SynDBSQLite3;

type
  TDBExportTablesForm = class(TForm)
    BtnExport: TButton;
    BtnCancel: TButton;
    GroupWhere: TGroupBox;






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
unit SynDBExplorerExportTables;

interface

uses
  Windows, Messages, SysUtils, CheckLst, Controls, StdCtrls, Classes, Graphics,
  Forms, ExtCtrls, mORMotUILogin,
  SynZip, SynCommons, SynDB, SynDBSQLite3;

type
  TDBExportTablesForm = class(TForm)
    BtnExport: TButton;
    BtnCancel: TButton;
    GroupWhere: TGroupBox;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unit SynDBExplorerFrame;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Grids, ExtCtrls, ComCtrls, Types, ShellAPI, Menus,
  SynCommons, SQLite3Commons, SynDB,
  SQLite3i18n, SQLite3UI, SQLite3UILogin, SQLite3ToolBar, SQLite3Pages;

type
  TDBExplorerFrame = class(TFrame)
    Splitter1: TSplitter;
    PanelClient: TPanel;
    Splitter2: TSplitter;
    DrawGrid: TDrawGrid;







|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unit SynDBExplorerFrame;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Grids, ExtCtrls, ComCtrls, Types, ShellAPI, Menus,
  SynCommons, SynDB,
  mORMot, mORMoti18n, mORMotUI, mORMotUILogin, mORMotToolBar, mORMotReport;

type
  TDBExplorerFrame = class(TFrame)
    Splitter1: TSplitter;
    PanelClient: TPanel;
    Splitter2: TSplitter;
    DrawGrid: TDrawGrid;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unit SynDBExplorerMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Grids, ExtCtrls, StdCtrls, Consts,
  SynCommons, SQLite3Commons, SynSQLite3, SynZip,
  SQLite3i18n, SQLite3UI, SQLite3UIEdit, SQLite3UILogin, SQLite3ToolBar,
  SynTaskDialog,  SynDB, SynDBOracle, SynOleDB, SynDBSQLite3, SynDBODBC,
  SynDBExplorerClasses, SynDBExplorerFrame, ComCtrls;

type
  TDbExplorerMain = class(TForm)
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);







|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unit SynDBExplorerMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Grids, ExtCtrls, StdCtrls, Consts,
  SynCommons, mORMot, SynSQLite3, SynZip,
  mORMoti18n, mORMotUI, mORMotUIEdit, mORMotUILogin, mORMotToolBar,
  SynTaskDialog,  SynDB, SynDBOracle, SynOleDB, SynDBSQLite3, SynDBODBC,
  SynDBExplorerClasses, SynDBExplorerFrame, ComCtrls;

type
  TDbExplorerMain = class(TForm)
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);

Changes to SQLite3/Samples/14 - Interface based services/Project14ClientMain.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
unit Project14ClientMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, SQLite3Commons, SQLite3HttpClient,
  Project14Interface;

type
  TForm1 = class(TForm)
    edtA: TEdit;
    edtB: TEdit;
    lblA: TLabel;
................................................................................
    edtB.SetFocus;
    exit;
  end;
  if Client=nil then begin
    if Model=nil then
      Model := TSQLModel.Create([],ROOT_NAME);
    case ComboProtocol.ItemIndex of
    0: Client := TSQLite3HttpClient.Create('localhost','888',Model);
    1: Client := TSQLRestClientURINamedPipe.Create(Model,APPLICATION_NAME);
    else exit;
    end;
    Client.SetUser('User','synopse');
    Client.ServiceRegister([TypeInfo(ICalculator)],sicShared);
  end;
  if Client.Services['Calculator'].Get(I) then







|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
unit Project14ClientMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  SynCommons, mORMot, mORMotHttpClient,
  Project14Interface;

type
  TForm1 = class(TForm)
    edtA: TEdit;
    edtB: TEdit;
    lblA: TLabel;
................................................................................
    edtB.SetFocus;
    exit;
  end;
  if Client=nil then begin
    if Model=nil then
      Model := TSQLModel.Create([],ROOT_NAME);
    case ComboProtocol.ItemIndex of
    0: Client := TSQLHttpClient.Create('localhost','888',Model);
    1: Client := TSQLRestClientURINamedPipe.Create(Model,APPLICATION_NAME);
    else exit;
    end;
    Client.SetUser('User','synopse');
    Client.ServiceRegister([TypeInfo(ICalculator)],sicShared);
  end;
  if Client.Services['Calculator'].Get(I) then

Changes to SQLite3/Samples/14 - Interface based services/Project14Server.dpr.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
program Project14Server;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  SQLite3Commons,
  SQLite3,
  Project14Interface;

type
  TServiceCalculator = class(TInterfacedObject, ICalculator)
  public
    function Add(n1,n2: integer): integer;
  end;






|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
program Project14Server;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  mORMot,
  mORMotSQLite3,
  Project14Interface;

type
  TServiceCalculator = class(TInterfacedObject, ICalculator)
  public
    function Add(n1,n2: integer): integer;
  end;

Changes to SQLite3/Samples/14 - Interface based services/Project14ServerHttp.dpr.

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
..
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  SynCommons,
  SQLite3Commons,
  SQLite3HttpServer,
  Project14Interface;

type
  TServiceCalculator = class(TInterfacedObject, ICalculator)
  public
    function Add(n1,n2: integer): integer;
  end;
................................................................................
  result := n1+n2;
end;


var
  aModel: TSQLModel;
  aServer: TSQLRestServer;
  aHTTPServer: TSQLite3HttpServer;
begin
  aModel := TSQLModel.Create([],ROOT_NAME);
  try
    aServer := TSQLRestServerFullMemory.Create(aModel,'test.json',false,true);
    try
      aServer.ServiceRegister(TServiceCalculator,[TypeInfo(ICalculator)],sicShared);
      aHTTPServer := TSQLite3HttpServer.Create('888',[aServer]);
      try
        writeln('Background server is running.'#10);
        write('Press [Enter] to close the server.');
        readln;
      finally
        aHTTPServer.Free;
      end;







|
|







 







|






|







3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
..
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  SynCommons,
  mORMot,
  mORMotHttpServer,
  Project14Interface;

type
  TServiceCalculator = class(TInterfacedObject, ICalculator)
  public
    function Add(n1,n2: integer): integer;
  end;
................................................................................
  result := n1+n2;
end;


var
  aModel: TSQLModel;
  aServer: TSQLRestServer;
  aHTTPServer: TSQLHttpServer;
begin
  aModel := TSQLModel.Create([],ROOT_NAME);
  try
    aServer := TSQLRestServerFullMemory.Create(aModel,'test.json',false,true);
    try
      aServer.ServiceRegister(TServiceCalculator,[TypeInfo(ICalculator)],sicShared);
      aHTTPServer := TSQLHttpServer.Create('888',[aServer]);
      try
        writeln('Background server is running.'#10);
        write('Press [Enter] to close the server.');
        readln;
      finally
        aHTTPServer.Free;
      end;

Changes to SQLite3/Samples/14 - Interface based services/Project14ServerInMemory.dpr.

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  SynCommons,
  SQLite3Commons,
  Project14Interface;

type
  TServiceCalculator = class(TInterfacedObject, ICalculator)
  public
    function Add(n1,n2: integer): integer;
  end;







|







4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  SynCommons,
  mORMot,
  Project14Interface;

type
  TServiceCalculator = class(TInterfacedObject, ICalculator)
  public
    function Add(n1,n2: integer): integer;
  end;

Changes to SQLite3/Samples/15 - External DB performance/PerfMain.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unit PerfMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Contnrs, XPMan, ShellApi,
  SynCommons, SQLite3Commons, SynSQLite3, SQLite3, SQLite3DB,
  SynDB, SynDBSQLite3, SynDBOracle, SynOleDB, SynDBODBC;

type
  TMainForm = class(TForm)
    LogMemo: TMemo;
    OraTNSName: TEdit;
    OraUser: TEdit;







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unit PerfMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Contnrs, XPMan, ShellApi,
  SynCommons, SynSQLite3, mORMot, mORMotSQLite3, mORMotDB,
  SynDB, SynDBSQLite3, SynDBOracle, SynOleDB, SynDBODBC;

type
  TMainForm = class(TForm)
    LogMemo: TMemo;
    OraTNSName: TEdit;
    OraUser: TEdit;

Changes to SQLite3/Samples/MainDemo/FileClient.pas.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
uses
  Windows,
  SysUtils,
  Classes,
  Graphics,
  SynCommons,
  SynGdiPlus,
  SQLite3Commons,
  SQLite3HttpClient,
  SQLite3i18n,
  SQLite3ToolBar,
  SQLite3Pages,
  FileTables;

type
  /// a HTTP/1.1 client to access SynFile
  TFileClient = class(TSQLite3HttpClient)
  public
    /// initialize the Client for a specified network Server name
    constructor Create(const aServer: AnsiString); reintroduce;
	/// used internaly to retrieve a given action
    function OnSetAction(TableIndex, ToolbarIndex: integer; TestEnabled: boolean;
      var Action): string;
	/// client-side access to the remote RESTful service







|
|
|
|
|




|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
uses
  Windows,
  SysUtils,
  Classes,
  Graphics,
  SynCommons,
  SynGdiPlus,
  mORMot,
  mORMotHttpClient,
  mORMoti18n,
  mORMotToolBar,
  mORMotReport,
  FileTables;

type
  /// a HTTP/1.1 client to access SynFile
  TFileClient = class(TSQLHttpClient)
  public
    /// initialize the Client for a specified network Server name
    constructor Create(const aServer: AnsiString); reintroduce;
	/// used internaly to retrieve a given action
    function OnSetAction(TableIndex, ToolbarIndex: integer; TestEnabled: boolean;
      var Action): string;
	/// client-side access to the remote RESTful service

Changes to SQLite3/Samples/MainDemo/FileEdit.pas.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ExtDlgs,
{$ifdef USETMSPACK}
  TaskDialog,
{$endif}
  SynCommons, SynCrypto, SynGdiPlus, SynTaskDialog,
  SQLite3Commons, SQLite3UILogin, SQLite3UI, SQLite3i18n,
  FileTables;

type
  /// SynFile Edit window
  // - we don't use the standard Window generation (from SQLite3UIEdit),
  // but a custom window, created as RAD
  TEditForm = class(TVistaForm)
    Name: TLabeledEdit;
    KeyWords: TLabeledEdit;
    Memo: TMemo;
    procedure FormShow(Sender: TObject);
    procedure BtnOkClick(Sender: TObject);







|




|







6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ExtDlgs,
{$ifdef USETMSPACK}
  TaskDialog,
{$endif}
  SynCommons, SynCrypto, SynGdiPlus, SynTaskDialog,
  mORMot, mORMotUILogin, mORMotUI, mORMoti18n,
  FileTables;

type
  /// SynFile Edit window
  // - we don't use the standard Window generation (from mORMotUIEdit),
  // but a custom window, created as RAD
  TEditForm = class(TVistaForm)
    Name: TLabeledEdit;
    KeyWords: TLabeledEdit;
    Memo: TMemo;
    procedure FormShow(Sender: TObject);
    procedure BtnOkClick(Sender: TObject);

Changes to SQLite3/Samples/MainDemo/FileMain.pas.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
{$ifdef USETMSPACK}
  AdvToolBar, AdvPreviewMenu, AdvShapeButton, AdvOfficePager,
{$endif}
  ImgList, ShellApi,
  SynCommons, SynGdiPlus, SQLite3Commons, SQLite3HttpClient,
  SQLite3ToolBar, SQLite3UI, SQLite3UILogin, SQLite3i18n,
{$ifdef DEBUGINTERNALSERVER}
  FileServer,
{$endif}
  FileTables, FileClient, FileEdit;

type
  /// SynFile main Window







|
|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
{$ifdef USETMSPACK}
  AdvToolBar, AdvPreviewMenu, AdvShapeButton, AdvOfficePager,
{$endif}
  ImgList, ShellApi,
  SynCommons, SynGdiPlus, mORMot, mORMotHttpClient,
  mORMotToolBar, mORMotUI, mORMotUILogin, mORMoti18n,
{$ifdef DEBUGINTERNALSERVER}
  FileServer,
{$endif}
  FileTables, FileClient, FileEdit;

type
  /// SynFile main Window

Changes to SQLite3/Samples/MainDemo/FileServer.pas.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

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

uses
  SysUtils,
  Classes,
  SynCommons,
  SQLite3Commons,
  SQLite3i18n,
  SQLite3HttpServer,
  SQLite3,
  FileTables;

type
  /// a server to access SynFile data content
  TFileServer = class(TSQLRestserverDB)
  private
    fTempAuditTrail: TSQLAuditTrail;
  public
    /// the runing HTTP/1.1 server
    Server: TSQLite3HttpServer;
  	/// create the database and HTTP/1.1 server
    constructor Create;
	  /// release used memory and data
    destructor Destroy; override;
  	/// add a row to the TSQLAuditTrail table
    procedure AddAuditTrail(aEvent: TFileEvent; const aMessage: RawUTF8='';
      aAssociatedRecord: TRecordReference=0);
................................................................................
  Add(fTempAuditTrail,true);
end;

constructor TFileServer.Create;
begin
  inherited Create(CreateFileModel(self),ChangeFileExt(paramstr(0),'.db3'));
  CreateMissingTables(ExeVersion.Version.Version32);
  Server := TSQLite3HttpServer.Create(SERVER_HTTP_PORT,self);
  AddAuditTrail(feServerStarted);
  OnUpdateEvent := OnDatabaseUpdateEvent;
end;

destructor TFileServer.Destroy;
begin
  try







|
|
|
|









|







 







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

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

uses
  SysUtils,
  Classes,
  SynCommons,
  mORMot,
  mORMoti18n,
  mORMotHttpServer,
  mORMotSQLite3,
  FileTables;

type
  /// a server to access SynFile data content
  TFileServer = class(TSQLRestserverDB)
  private
    fTempAuditTrail: TSQLAuditTrail;
  public
    /// the runing HTTP/1.1 server
    Server: TSQLHttpServer;
  	/// create the database and HTTP/1.1 server
    constructor Create;
	  /// release used memory and data
    destructor Destroy; override;
  	/// add a row to the TSQLAuditTrail table
    procedure AddAuditTrail(aEvent: TFileEvent; const aMessage: RawUTF8='';
      aAssociatedRecord: TRecordReference=0);
................................................................................
  Add(fTempAuditTrail,true);
end;

constructor TFileServer.Create;
begin
  inherited Create(CreateFileModel(self),ChangeFileExt(paramstr(0),'.db3'));
  CreateMissingTables(ExeVersion.Version.Version32);
  Server := TSQLHttpServer.Create(SERVER_HTTP_PORT,self);
  AddAuditTrail(feServerStarted);
  OnUpdateEvent := OnDatabaseUpdateEvent;
end;

destructor TFileServer.Destroy;
begin
  try

Changes to SQLite3/Samples/MainDemo/FileTables.pas.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

uses
  SysUtils,
  Classes,
  SynCommons,
  SynCrypto,
  SynZip,
  SQLite3Commons,
  SQLite3i18n;

type
  /// the internal events/states, as used by the TSQLAuditTrail table
  TFileEvent = (
    feUnknownState,
    feServerStarted,
    feServerShutdown,







|
|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

uses
  SysUtils,
  Classes,
  SynCommons,
  SynCrypto,
  SynZip,
  mORMot,
  mORMoti18n;

type
  /// the internal events/states, as used by the TSQLAuditTrail table
  TFileEvent = (
    feUnknownState,
    feServerStarted,
    feServerShutdown,

Changes to SQLite3/ServiceTestForm.pas.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  SysUtils,
  Classes,
  Forms,
  Controls,
  StdCtrls,
  Dialogs,
  SynCrtSock,
  SQLite3Commons,
  WinSvc,
  SQLite3Service;
  
const
  SERVICENAME = 'KOL_ServiceA';
  SERVICEEXE = 'd:\temp\debug\TestKOLService.exe';

type
  /// form to test the service remote control







|

|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  SysUtils,
  Classes,
  Forms,
  Controls,
  StdCtrls,
  Dialogs,
  SynCrtSock,
  mORMot,
  WinSvc,
  mORMotService;
  
const
  SERVICENAME = 'KOL_ServiceA';
  SERVICEEXE = 'd:\temp\debug\TestKOLService.exe';

type
  /// form to test the service remote control

Changes to SQLite3/ServiceTestSQL3.dpr.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// test the service remote control of the SQLite3Service unit
program ServiceTestSQL3;

uses
  Forms,
  ServiceTestForm in 'ServiceTestForm.pas' {MainServiceTest};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TMainServiceTest, MainServiceTest);
  Application.Run;
end.
|













1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// test the service remote control of the mORMotService unit
program ServiceTestSQL3;

uses
  Forms,
  ServiceTestForm in 'ServiceTestForm.pas' {MainServiceTest};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TMainServiceTest, MainServiceTest);
  Application.Run;
end.

Changes to SQLite3/TestOleDB.dpr.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  SynCommons,
{$ifndef DELPHI5OROLDER}
  SQLite3Commons,
{$endif}
  SynDB,
  SynDBOracle,
  SynOleDB;

var Props: TOleDBConnectionProperties;








|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes,
  SynCommons,
{$ifndef DELPHI5OROLDER}
  mORMot,
{$endif}
  SynDB,
  SynDBOracle,
  SynOleDB;

var Props: TOleDBConnectionProperties;

Changes to SQLite3/TestSQL3.dpr.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
55
56
57
58
59
60
61



62
63
64
65
66
67
68
..
72
73
74
75
76
77
78
79
80
81
82
83
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  - SynSQLite3 logic extracted from SQLite3 unit
  - enhanced tests about external database handling
  - tests renamed to match the new "mORMot" framework name

  Version 1.16
  - all tests are now implemented in a separated SQLite3SelfTests unit -
    this is requested by Delphi XE2 background compiler issues 





  this application has EnableMemoryLeakReporting conditional defined in its
  Project/Options -> we can therefore ensure that our mORMot Client/Server
  framework classes have no memory leak
  - Search Path and Debug Path: [\Dev\Lib\LVCL]
  - Conditional defines: EnableMemoryLeakReporting[;ENHANCEDRTL][;LVCL]
................................................................................
  - first line of uses clause must be  {$I SynDprUses.inc}  to enable FastMM4 *)


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

uses
  {$I SynDprUses.inc}
  SQLite3SelfTests in 'SQLite3SelfTests.pas';

begin
  SQLite3ConsoleTests;         
end.







|







 







>
>
>







 







|




17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
..
75
76
77
78
79
80
81
82
83
84
85
86
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  - SynSQLite3 logic extracted from SQLite3 unit
  - enhanced tests about external database handling
  - tests renamed to match the new "mORMot" framework name

  Version 1.16
  - all tests are now implemented in a separated SQLite3SelfTests unit -
    this is requested by Delphi XE2 background compiler issues 
	
  Version 1.18
  - renamed SQLite3*.pas units to mORMot*.pas


  this application has EnableMemoryLeakReporting conditional defined in its
  Project/Options -> we can therefore ensure that our mORMot Client/Server
  framework classes have no memory leak
  - Search Path and Debug Path: [\Dev\Lib\LVCL]
  - Conditional defines: EnableMemoryLeakReporting[;ENHANCEDRTL][;LVCL]
................................................................................
  - first line of uses clause must be  {$I SynDprUses.inc}  to enable FastMM4 *)


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

uses
  {$I SynDprUses.inc}
  mORMotSelfTests in 'mORMotSelfTests.pas';

begin
  SQLite3ConsoleTests;         
end.

Changes to SQLite3/TestSQL3Register.dpr.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

var delete: boolean;
begin
  // perform url registration for http.sys
  // (e.g. to be run as administrator under Windows Vista/Seven)
  delete := (ParamCount=1) and SameText(ParamStr(1),'/DELETE');
  // parameters below must match class function
  //  TTestClientServerAccess.RegisterAddUrl in SQLite3HttpServer.pas:
  writeln(REGSTR[delete],' of /root:888/+ for http.sys'); 
  writeln(THttpApiServer.AddUrlAuthorize('root','888',false,'+',delete));
  // we're done
  WriteLn('Done - Press ENTER to Exit');
  ReadLn;
end.
 







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

var delete: boolean;
begin
  // perform url registration for http.sys
  // (e.g. to be run as administrator under Windows Vista/Seven)
  delete := (ParamCount=1) and SameText(ParamStr(1),'/DELETE');
  // parameters below must match class function
  //  TTestClientServerAccess.RegisterAddUrl in mORMotHttpServer.pas:
  writeln(REGSTR[delete],' of /root:888/+ for http.sys'); 
  writeln(THttpApiServer.AddUrlAuthorize('root','888',false,'+',delete));
  // we're done
  WriteLn('Done - Press ENTER to Exit');
  ReadLn;
end.
 

Name change from SQLite3/SQLite3Commons.pas to SQLite3/mORMot.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
675
676
677
678
679
680
681

682
683
684
685
686
687
688
...
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
...
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
....
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
....
9924
9925
9926
9927
9928
9929
9930
9931
9932
9933
9934
9935
9936
9937
9938
/// Common ORM and SOA classes
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3Commons;

(*
    This file is part of Synopse mORMot database framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


       Commons routines for a SQLite3-based database framework
      *********************************************************

    - fast UTF-8 / Unicode / WinAnsi types and conversion routines
    - RawUnicode support (similar to UnicodeString in Delphi 2009)
    - ISO 8601:2004 functions to properly handle date/time values in TEXT field
    - TSQLTable class to handle SQLite3-like table results (can then be
      displayed with SQLite3UI unit, or search - with Soundex if wanted)
    - TSQLTableJSON and TSQLJSONWriter classes to transmit results in JSON format
    - inherit TSQLRecord to define Table structure once in some published
      properties and easily interact with the server (alla Ruby on Rails),
      making this framework implements a full Object-relational mapping
    - TSQLRestClient and TSQLRestServer to handle a REST client/server protocol
      with GET/POST/PUT/DELETE methods
    - if REST protocol is not enough (JOIN e.g.) a full SQL statement can be used
    - locking is handled at record level, with new LOCK and UNLOCK REST methods
      (SQLite3 only handle database level lock)
    - fast transaction is available, with new BEGIN, END and ABORT REST methods
    - after a record deletion, ensure relational database coherency
    - TSQLRestServerStaticInMemory implements a basic REST in-memory database
      engine (no need to use SQLite3 database engine for simplier applications)
    - client/server access can be used through a fast direct local DLL export
    - client/server local or remote access can be used through a named pipe
      or HTTP/1.1 protocol (with unit SQLite3HttpServer/SQlite3HttpClient)
    - smart update of any result table or record (retrieve data only if changed)
    - very fast "stored procedure" feature, coded in Delphi

      Initial version: 2008 March, by Arnaud Bouchez

    Version 1.1 - 14 January 2010:
    - SQLite3 database layer updated to version 3.6.22
    - new communication layer, to be used on a local computer: windows messages
      (see TSQLRestClientURIMessage class). On a local machine, this is faster
................................................................................
      TSQLRecordMany) to be used with VirtualTableExternalRegister() - there was
      indeed no implementation requirement to force a specific class type
    - added aUseBatchMode optional parameter to TSQLRecordMany.ManyDelete() method
    - now JSON parser will handle #1..' ' chars as whitespace (not only ' ')
    - now huge service JSON response is truncated (to default 20 KB) in logs

  Version 1.18

    - deep code refactoring, introducing TSQLPropInfo* classes in order to
      decouple the ORM definitions from the RTTI - will allow definition of
      any class members, even if there is no RTTI generated or via custom
      properties attributes or a fluent interface 
    - introduced more readable "stored AS_UNIQUE" published property definition
      in TSQLRecord (via the explicit AS_UNIQUE=false constant)
    - introducing TInterfaceStub and TInterfaceMock classes to define
................................................................................
      (as in json.org specifications)
    - SetWeakZero() function will now use a much faster per-class lock design
    - extraction of TTestLowLevelTypes and TTestBasicClasses code into
      SynSelfTests.pas unit
    - allow only to delete its own session - security fix for ticket [7723fa7ebd]
    - fix TSQLRestClientURI.Commit/RollBack to work as expected


  String usage in the Synopse SQLite3 database framework:
    - RawUTF8 is used for every internal data usage, since both SQLite3 and
      JSON do expect UTF-8 encoding
    - WinAnsiString where WinAnsi-encoded AnsiString (code page 1252) are needed
    - generic string for i18n (in unit SQLite3i18n), i.e. text ready to be
      used within the VCL, as either AnsiString (for Delphi 2 to 2007) or
      UnicodeString (for Delphi 2009+)
    - RawUnicode in some technical places (e.g. direct Win32 *W() API call in
      Delphi 7) - note: this type is NOT compatible with Delphi 2009+
      UnicodeString (our RawUnicode is an AnsiString, i.e. with elemSize=1,
      whereas UnicodeString has elemSize=2)
    - RawByteString for byte storage (e.g. for FileFromString() function)
    - SynUnicode is the fastest available Unicode native string type:
      this type is native to the compiler, so you can use Length() Copy() and
      such functions with it (this is not possible with RawUnicodeString type),
      but it will map slow WideString before Delphi 2009
    - use special conversion functions for Delphi 2009+ UnicodeString
      (defined inside {$ifdef UNICODE}...{$endif} blocks below)
    - TFileName for file names
    - never use AnsiString directly, but only specific RawUTF8, WinAnsiString,
      RawUnicode, RawByteString sub-types
    - our Synopse PDF Library use its own PDFString type

  TODO:
    - test and enhance Free Pascal Compiler compatibiliby (2.4.0 and later)
    - port to other OS, using CrossKylix or Free Pascal Compiler

*)


................................................................................
    sftRecord,
    /// an INTEGER field for a boolean value: 0 is FALSE, anything else TRUE
    // (encoded as JSON 'true' or 'false' constants)
    sftBoolean,
    /// a FLOAT (floating point double precision, cf. SQLite3) field
    sftFloat,
    /// a ISO 8601 encoded TEXT field - SQLite3 compatible;
    // a ISO8601 collation is forced
    // (TDateTime Delphi property)
    sftDateTime,
    /// an INTEGER field for coding a date and time - not SQLite3 compatible
    // - TTimeLog=Int64 Delphi property which can be typecasted to Iso8601
    sftTimeLog,
    /// a FLOAT containing a 4 decimals floating point value
    // (Currency Delphi property minimizes rounding errors in monetary
    // calculations which may occur with sftFloat type)
................................................................................
      UpdateSet: array[boolean] of RawUTF8;
      /// all fields, excluding the ID field, exposed as 'COL1,COL2'
      // - to be used e.g. for INSERT statements
      InsertSet: RawUTF8;
    end;
    /// opaque structure used on the Server side to specify e.g. the DB connection
    // - will define such a generic TObject, to avoid any unecessary dependency
    // to the SynDB unit in SQLite3Commons
    // - in practice, will be assigned by VirtualTableExternalRegister() to
    // a TSQLDBConnectionProperties instance
    ExternalDatabase: TObject;
    /// used on the Server side to specify the external DB table name
    // - e.g. for including a schema name or an existing table name, with an
    // OleDB/MSSQL/Oracle/Jet/SQLite3 backend
    // - equals SQLTableName by default (may be overriden e.g. by SQLite3DB's
................................................................................


{ ************ Logging classes and functions }

type
  /// logging class with enhanced RTTI
  // - will write TObject/TSQLRecord, enumerations and sets content as JSON
  // - is the default logging family used by SQLite3Commons/SQLite3
  TSQLLog = class(TSynLog)
  protected
    procedure CreateLogWriter; override;
    procedure AddTyped(aTypeInfo: pointer; var aValue); override;
  end;

{$ifdef WITHLOG}
|


|


|







 







|







 







|
|

|
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>







 







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







 







|
|







 







|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
















57
58
59
60
61
62
63
...
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
...
726
727
728
729
730
731
732
























733
734
735
736
737
738
739
...
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
....
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
....
9885
9886
9887
9888
9889
9890
9891
9892
9893
9894
9895
9896
9897
9898
9899
/// Common ORM and SOA classes for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMot;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


       Client-Server ORM and SOA kernel of the mORMot framework
      **********************************************************

	- Client-Server classes using a RESTful model via JSON, over named pipes
	  or GDI messages  (HTTP/1.1 protocol with unit mORMotHttpServer/Client)
	- Client-Server ORM via TSQLRecord classes definition
	- Client-Server interface-based services for SOA process
	- optimized low-level RTTI and JSON process (TSQLTable)
	- ready to be used by mORMotSQLite3.pas with a SQLite3 engine
	- implements in-memory database tables with JSON/binary disk persistence

















      Initial version: 2008 March, by Arnaud Bouchez

    Version 1.1 - 14 January 2010:
    - SQLite3 database layer updated to version 3.6.22
    - new communication layer, to be used on a local computer: windows messages
      (see TSQLRestClientURIMessage class). On a local machine, this is faster
................................................................................
      TSQLRecordMany) to be used with VirtualTableExternalRegister() - there was
      indeed no implementation requirement to force a specific class type
    - added aUseBatchMode optional parameter to TSQLRecordMany.ManyDelete() method
    - now JSON parser will handle #1..' ' chars as whitespace (not only ' ')
    - now huge service JSON response is truncated (to default 20 KB) in logs

  Version 1.18
    - renamed SQLite3Commons.pas to mORMot.pas
    - deep code refactoring, introducing TSQLPropInfo* classes in order to
      decouple the ORM definitions from the RTTI - will allow definition of
      any class members, even if there is no RTTI generated or via custom
      properties attributes or a fluent interface 
    - introduced more readable "stored AS_UNIQUE" published property definition
      in TSQLRecord (via the explicit AS_UNIQUE=false constant)
    - introducing TInterfaceStub and TInterfaceMock classes to define
................................................................................
      (as in json.org specifications)
    - SetWeakZero() function will now use a much faster per-class lock design
    - extraction of TTestLowLevelTypes and TTestBasicClasses code into
      SynSelfTests.pas unit
    - allow only to delete its own session - security fix for ticket [7723fa7ebd]
    - fix TSQLRestClientURI.Commit/RollBack to work as expected

























  TODO:
    - test and enhance Free Pascal Compiler compatibiliby (2.4.0 and later)
    - port to other OS, using CrossKylix or Free Pascal Compiler

*)


................................................................................
    sftRecord,
    /// an INTEGER field for a boolean value: 0 is FALSE, anything else TRUE
    // (encoded as JSON 'true' or 'false' constants)
    sftBoolean,
    /// a FLOAT (floating point double precision, cf. SQLite3) field
    sftFloat,
    /// a ISO 8601 encoded TEXT field - SQLite3 compatible;
    // - a ISO8601 collation is forced 
	// - corresponds to a TDateTime Delphi property
    sftDateTime,
    /// an INTEGER field for coding a date and time - not SQLite3 compatible
    // - TTimeLog=Int64 Delphi property which can be typecasted to Iso8601
    sftTimeLog,
    /// a FLOAT containing a 4 decimals floating point value
    // (Currency Delphi property minimizes rounding errors in monetary
    // calculations which may occur with sftFloat type)
................................................................................
      UpdateSet: array[boolean] of RawUTF8;
      /// all fields, excluding the ID field, exposed as 'COL1,COL2'
      // - to be used e.g. for INSERT statements
      InsertSet: RawUTF8;
    end;
    /// opaque structure used on the Server side to specify e.g. the DB connection
    // - will define such a generic TObject, to avoid any unecessary dependency
    // to the SynDB unit in mORMot.pas
    // - in practice, will be assigned by VirtualTableExternalRegister() to
    // a TSQLDBConnectionProperties instance
    ExternalDatabase: TObject;
    /// used on the Server side to specify the external DB table name
    // - e.g. for including a schema name or an existing table name, with an
    // OleDB/MSSQL/Oracle/Jet/SQLite3 backend
    // - equals SQLTableName by default (may be overriden e.g. by SQLite3DB's
................................................................................


{ ************ Logging classes and functions }

type
  /// logging class with enhanced RTTI
  // - will write TObject/TSQLRecord, enumerations and sets content as JSON
  // - is the default logging family used by the mORMot framework
  TSQLLog = class(TSynLog)
  protected
    procedure CreateLogWriter; override;
    procedure AddTyped(aTypeInfo: pointer; var aValue); override;
  end;

{$ifdef WITHLOG}

Name change from SQLite3/SQlite3BigTable.pas to SQLite3/mORMotBigTable.pas.

1
2
3
4
5
6
7
8
9
10
11
..
39
40
41
42
43
44
45




46
47



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/// Virtual tables for Synopse Big Table access for the mORMot framework
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.15
unit SQLite3BigTable;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  decision by deleting the provisions above and replace them with the notice
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****





  Version 1.15
  - first public release, corresponding to mORMot Framework 1.15




}

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

interface

uses
  Windows,
  SysUtils,
  Classes,
  SynCommons,
  SQLite3Commons,
  SynBigTable;

type
  /// REST server with direct access to a Synopse Big Table external database
  // - handle all REST commands via direct TSynBigTableMetaData or
  // TSynBigTableRecord call (using a TSynBigTableTable instance)
  // - is used by TSQLRestServer.URI for faster RESTful direct access
|

|
|







 







>
>
>
>


>
>
>












|







1
2
3
4
5
6
7
8
9
10
11
..
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/// Virtual Tables for Synopse Big Table access for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotBigTable;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  decision by deleting the provisions above and replace them with the notice
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****

  
    IN WORK / UNFINISHED PROCESS: DO NOT USE THIS UNIT!
   *****************************************************
	
  Version 1.15
  - first public release, corresponding to mORMot Framework 1.15
  
  Version 1.18
  - unit SQLite3BigTable.pas renamed mORMotBigTable.pas

}

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

interface

uses
  Windows,
  SysUtils,
  Classes,
  SynCommons,
  mORMot,
  SynBigTable;

type
  /// REST server with direct access to a Synopse Big Table external database
  // - handle all REST commands via direct TSynBigTableMetaData or
  // TSynBigTableRecord call (using a TSynBigTableTable instance)
  // - is used by TSQLRestServer.URI for faster RESTful direct access

Name change from SQLite3/SQLite3DB.pas to SQLite3/mORMotDB.pas.

1
2
3
4
5
6
7
8
9
10
11
..
72
73
74
75
76
77
78

79
80
81
82
83
84
85
..
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
...
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
/// Virtual tables for DB direct access classes for the mORMot framework
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3DB;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  - removed TSQLRecordExternal class type, to allow any TSQLRecord (e.g.
    TSQLRecordMany) to be used with VirtualTableExternalRegister() - there was
    indeed no implementation requirement to force a specific class type
  - now create properly UNIQUE fields (i.e. "stored AS_UNIQUE") in external tables
  - handle NULL values for BLOBs as expected

  Version 1.18

  - new function VirtualTableExternalRegisterAll(), to register all tables
    of a mORMot model to be handled via a specified database
  - now TSQLRestServerStaticExternal won't create any columns for external
    tables with unsupported published property types (sftUnknown or sftMany),
    just like TSQLRecord.GetSQLCreate() method
  - fixed issue in TSQLRestServerStaticExternal.EngineDeleteWhere() when
    calling commands like MyDB.Delete(TSQLMyClass, 'PLU < ?', [20000])
................................................................................
interface

uses
  Windows,
  SysUtils,
  Classes,
  SynCommons,
  SQLite3Commons,
  SynDB;

type
  /// REST server with direct access to a SynDB-based external database
  // - handle all REST commands, using the external SQL database connection,
  // and prepared statements
  // - is used by TSQLRestServer.URI for faster RESTful direct access
................................................................................
function TSQLRestServerStaticExternal.ExecuteInlined(const aSQL: RawUTF8;
  ExpectResults: Boolean): ISQLDBRows;
var Query: TSQLDBStatement;
    i, maxParam: integer;
    Types: TSQLFieldTypeArray; // sftInteger, sftFloat, sftUTF8Text, sftBlob
    Values: TRawUTF8DynArray;
    GenericSQL: RawUTF8;
begin
  result := nil; // returns nil interface on error
  if self=nil then
    exit;
  if (not ExpectResults) and (Owner<>nil) then
    Owner.FlushInternalDBCache; // add/update/delete should flush DB cache
  // convert inlined :(1234): parameters into Values[] for Bind*() calls
  GenericSQL := ExtractInlineParameters(aSQL,Types,Values,maxParam);
|


|







 







>







 







|







 







|







1
2
3
4
5
6
7
8
9
10
11
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
...
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
/// Virtual Tables for external DB access for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotDB;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  - removed TSQLRecordExternal class type, to allow any TSQLRecord (e.g.
    TSQLRecordMany) to be used with VirtualTableExternalRegister() - there was
    indeed no implementation requirement to force a specific class type
  - now create properly UNIQUE fields (i.e. "stored AS_UNIQUE") in external tables
  - handle NULL values for BLOBs as expected

  Version 1.18
  - unit SQLite3DB.pas renamed mORMotDB.pas
  - new function VirtualTableExternalRegisterAll(), to register all tables
    of a mORMot model to be handled via a specified database
  - now TSQLRestServerStaticExternal won't create any columns for external
    tables with unsupported published property types (sftUnknown or sftMany),
    just like TSQLRecord.GetSQLCreate() method
  - fixed issue in TSQLRestServerStaticExternal.EngineDeleteWhere() when
    calling commands like MyDB.Delete(TSQLMyClass, 'PLU < ?', [20000])
................................................................................
interface

uses
  Windows,
  SysUtils,
  Classes,
  SynCommons,
  mORMot,
  SynDB;

type
  /// REST server with direct access to a SynDB-based external database
  // - handle all REST commands, using the external SQL database connection,
  // and prepared statements
  // - is used by TSQLRestServer.URI for faster RESTful direct access
................................................................................
function TSQLRestServerStaticExternal.ExecuteInlined(const aSQL: RawUTF8;
  ExpectResults: Boolean): ISQLDBRows;
var Query: TSQLDBStatement;
    i, maxParam: integer;
    Types: TSQLFieldTypeArray; // sftInteger, sftFloat, sftUTF8Text, sftBlob
    Values: TRawUTF8DynArray;
    GenericSQL: RawUTF8;
begin                               
  result := nil; // returns nil interface on error
  if self=nil then
    exit;
  if (not ExpectResults) and (Owner<>nil) then
    Owner.FlushInternalDBCache; // add/update/delete should flush DB cache
  // convert inlined :(1234): parameters into Values[] for Bind*() calls
  GenericSQL := ExtractInlineParameters(aSQL,Types,Values,maxParam);

Name change from SQLite3/SQLite3FastCgiServer.pas to SQLite3/mORMotFastCgiServer.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
66
67
68
69
70
71
72



73
74
75
76
77
78
79
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
...
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
/// HTTP/1.1 RESTFUL JSON mORMot Server implementation, using FASTCGI
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SQLite3FastCgiServer;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


 fastcgi configuration example for lighttpd under linux:
   - http://localhost/test will ask TSQLite3FastCGIServer from the content
   - we use only one process and one thread, which is OK for our framework
   - the socket is created by lighttpd, and used by the executable
   - the target executable (located at /var/bin/test) must call the
     SQLite3FastCGIMainProc() procedure
   - default implementation under linux use the libfcgi.so library:
     you'll need to exec e.g.  apt-get install libfcgi0ldbl

fastcgi.server = ( "/test/" =>
  ((  "socket" => "/tmp/test-socket",
      "bin-path" => "/var/bin/test",
  	  "min-procs" => 1,
................................................................................
  Version 1.5
  - new server class, using FASTCGI to communicate with an external
    dedicated HTTP Server (tested with lighttpd under Linux via CrossKylix
    compile), using the LibFastCgi external library
  - can use either the standard client library (libfcgi.dll or libfcgi.so),
    either an embedded 100% pascal FastCGI client (for Windows only) - not
    fully tested yet on real production project




}

{.$define USELIBFCGI}
// if defined, the standard client library (libfcgi.dll or libfcgi.so) is
// used, instead of the 100% pascal FastCGI client

................................................................................
  LibC,
{$endif}
{$ifndef USELIBFCGI}
  SynCrtSock, // the pascal version use our Synopse socket
{$endif}
  SysUtils, Classes,
  SynCommons,
  SQLite3Commons;


{{ HTTP/1.1 RESTFUL JSON SQLite3 Server classes, using FASTCGI
 - will raise an exception if the executable was not used as a FastCGI
   process, but a normal CGI process
 - call this procedure in your main block of your program: it is up to
   the HTTP server to implement the request handling }
procedure SQLite3FastCGIMainProc(Server: TSQLRestServer);



{$ifdef USELIBFCGI}

  (*   FastCGI access through the libfcgi library
         only needed libfcgi functions were defined
................................................................................
 /// An environment (as defined by environ(7))
 // - A nil-terminated array of strings, each string having the form name=value
  FCGX_ParamArrayType = PPAnsiChar;


/// returns TRUE if this process appears to be a CGI process
// - in such case, you should not use the FastCGI implementation
// - TSQLite3FastCGIServer will raise an exception in such case
function FCGX_IsCGI: boolean; cdecl; external dllname;

{{ accept a new request (NOT multi-thread safe)
 - return 0 for successful call, -1 for error
 - Finishes the request accepted by (and frees any storage allocated by) the
   previous call to FCGX_Accept. Creates input, output, and error streams and
   assigns them to *in, *out, and *err respectively.
................................................................................
{$endif}


implementation

{$ifdef USELIBFCGI}

procedure SQLite3FastCGIMainProc(Server: TSQLRestServer);
var _in, _out, _err: PFCGX_Stream;
    envp: FCGX_ParamArrayType;
{function ReadString: RawUTF8;
var tmp: array[0..1023] of AnsiChar; // 1KB should be enough for HTTP headers
    L: integer;
begin
  if FCGX_GetLine(tmp,sizeof(tmp),_in)=nil then
................................................................................
    with Server.URI(URL,Method,Content,Resp,Headers,SUPERVISOR_ACCESS_RIGHTS) do begin
      ContentLength := length(Resp);
      Headers := trim(Headers); // format headers
      if Headers<>'' then
        Headers := Headers+#13#10;
      Headers := FormatUTF8(
        'Status: %'#13#10'Server-InternalState: %'#13#10+
        'X-Powered-By: TSQLite3FastCGIServer http://synopse.info'#13#10+
        'Content-Type: '+JSON_CONTENT_TYPE+#13#10+
        'Content-Length: %'#13#10+
        '%'#13#10, // headers end with a void line
        [Lo,Hi,ContentLength,Headers]);
    end;
    // send the answer back to the HTTP server
    if FCGX_PutStr(pointer(Headers),length(Headers),_out)=length(Headers) then
................................................................................
    end;
  move(pointer(Name)^,P^,Len[0]);
  inc(P,Len[0]);
  move(pointer(Value)^,P^,Len[1]);
end;


procedure SQLite3FastCGIMainProc(Server: TSQLRestServer);
var FastCGI: TFastCGIServer;
begin
  FastCGI := TFastCGIServer.Create(Server);
  try
    FastCGI.Run;
  finally
    FastCGI.Free;
|

|
|







 







|







 







|



|







 







>
>
>







 







|


|




|







 







|







 







|







 







|







 







|







1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
...
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
/// FastCGI HTTP/1.1 Server implementation for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotFastCgiServer;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


 fastcgi configuration example for lighttpd under linux:
   - http://localhost/test will ask TSQLRestServer from the content
   - we use only one process and one thread, which is OK for our framework
   - the socket is created by lighttpd, and used by the executable
   - the target executable (located at /var/bin/test) must call the
     mORMotFastCGIMainProc() procedure
   - default implementation under linux use the libfcgi.so library:
     you'll need to exec e.g.  apt-get install libfcgi0ldbl

fastcgi.server = ( "/test/" =>
  ((  "socket" => "/tmp/test-socket",
      "bin-path" => "/var/bin/test",
  	  "min-procs" => 1,
................................................................................
  Version 1.5
  - new server class, using FASTCGI to communicate with an external
    dedicated HTTP Server (tested with lighttpd under Linux via CrossKylix
    compile), using the LibFastCgi external library
  - can use either the standard client library (libfcgi.dll or libfcgi.so),
    either an embedded 100% pascal FastCGI client (for Windows only) - not
    fully tested yet on real production project
	
  Version 1.18
  - renamed unit SQlite3FastCgiServer.pas to mORMotFastCgiServer.pas

}

{.$define USELIBFCGI}
// if defined, the standard client library (libfcgi.dll or libfcgi.so) is
// used, instead of the 100% pascal FastCGI client

................................................................................
  LibC,
{$endif}
{$ifndef USELIBFCGI}
  SynCrtSock, // the pascal version use our Synopse socket
{$endif}
  SysUtils, Classes,
  SynCommons,
  mORMot;


{{ publishes a HTTP/1.1 RESTFUL JSON mORMot Server, using FASTCGI
 - will raise an exception if the executable was not used as a FastCGI
   process, but a normal CGI process
 - call this procedure in your main block of your program: it is up to
   the HTTP server to implement the request handling }
procedure mORMotFastCGIMainProc(Server: TSQLRestServer);



{$ifdef USELIBFCGI}

  (*   FastCGI access through the libfcgi library
         only needed libfcgi functions were defined
................................................................................
 /// An environment (as defined by environ(7))
 // - A nil-terminated array of strings, each string having the form name=value
  FCGX_ParamArrayType = PPAnsiChar;


/// returns TRUE if this process appears to be a CGI process
// - in such case, you should not use the FastCGI implementation
// - caller should raise an exception in such case
function FCGX_IsCGI: boolean; cdecl; external dllname;

{{ accept a new request (NOT multi-thread safe)
 - return 0 for successful call, -1 for error
 - Finishes the request accepted by (and frees any storage allocated by) the
   previous call to FCGX_Accept. Creates input, output, and error streams and
   assigns them to *in, *out, and *err respectively.
................................................................................
{$endif}


implementation

{$ifdef USELIBFCGI}

procedure mORMotFastCGIMainProc(Server: TSQLRestServer);
var _in, _out, _err: PFCGX_Stream;
    envp: FCGX_ParamArrayType;
{function ReadString: RawUTF8;
var tmp: array[0..1023] of AnsiChar; // 1KB should be enough for HTTP headers
    L: integer;
begin
  if FCGX_GetLine(tmp,sizeof(tmp),_in)=nil then
................................................................................
    with Server.URI(URL,Method,Content,Resp,Headers,SUPERVISOR_ACCESS_RIGHTS) do begin
      ContentLength := length(Resp);
      Headers := trim(Headers); // format headers
      if Headers<>'' then
        Headers := Headers+#13#10;
      Headers := FormatUTF8(
        'Status: %'#13#10'Server-InternalState: %'#13#10+
        'X-Powered-By: mORMotFastCGIServer http://synopse.info'#13#10+
        'Content-Type: '+JSON_CONTENT_TYPE+#13#10+
        'Content-Length: %'#13#10+
        '%'#13#10, // headers end with a void line
        [Lo,Hi,ContentLength,Headers]);
    end;
    // send the answer back to the HTTP server
    if FCGX_PutStr(pointer(Headers),length(Headers),_out)=length(Headers) then
................................................................................
    end;
  move(pointer(Name)^,P^,Len[0]);
  inc(P,Len[0]);
  move(pointer(Value)^,P^,Len[1]);
end;


procedure mORMotFastCGIMainProc(Server: TSQLRestServer);
var FastCGI: TFastCGIServer;
begin
  FastCGI := TFastCGIServer.Create(Server);
  try
    FastCGI.Run;
  finally
    FastCGI.Free;

Name change from SQLite3/SQLite3HttpClient.pas to SQLite3/mORMotHttpClient.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
91
92
93
94
95
96
97




98
99
100
101
102
103
104
...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
...
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
...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
...
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/// HTTP/1.1 RESTFUL JSON mORMot Client classes
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SQLite3HttpClient;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****



        HTTP/1.1 RESTFUL JSON Client using SQLite3 database engine
    *******************************************************************

   - use internaly the JSON format for content communication
   - can be called by any JSON-aware AJAX application
   - can optionaly compress the returned data to optimize Internet bandwidth
   - speed is very high: more than 20MB/sec R/W localy on a 1.8GHz Sempron,
     i.e. 400Mb/sec of duplex raw IP data, with about 200 µs only elapsed
     by request (direct call is 50 µs, so bottle neck is the Win32 API),
................................................................................
      - fixed GPF issue at closing
      - fixed unnecessary dual URL signing (when authentication actived)

    Version 1.17
      - added optional aProxyName, aProxyByPass parameters to
        TSQLite3HttpClientWinGeneric / TSQLite3HttpClientWinINet and
        TSQLite3HttpClientWinHTTP constructors





}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
................................................................................
  SynZip,
{$endif}
{$ifdef COMPRESSSYNLZ}
  SynLZ,
{$endif}
  SynCrtSock,
  SynCommons,
  SQLite3Commons;

type
  /// Generic HTTP/1.1 RESTFUL JSON SQLite3 Client class
  TSQLite3HttpClientGeneric = class(TSQLRestClientURI)
  protected
    /// process low-level HTTP/1.1 request
    // - call by URI() public method
    // - returns 200,202,204 if OK, http status error otherwize in result.Lo
    // - returns Server-InternalState in result.Hi
    function InternalRequest(const url, method: RawUTF8;
      var Header, Data, DataType: RawUTF8): Int64Rec; virtual; abstract;
    /// method calling the RESTful server fServer via HTTP/1.1
    // - calls the InternalRequest() protected method
    function InternalURI(const url, method: RawUTF8; Resp: PRawUTF8=nil;
      Head: PRawUTF8=nil; SendData: PRawUTF8=nil): Int64Rec; override;
  public
    /// the time (in milliseconds) to keep the connection alive with the
    // TSQLite3HttpServer
    // - default is 20000, i.e. 20 seconds
    KeepAliveMS: cardinal;
  end;

  /// HTTP/1.1 RESTFUL JSON SQLite3 Client class using SynCrtSock / WinSock
  // - will give the best performance on a local computer, but has been found
  // out to be slower over a network
  // - is not able to use secure HTTPS protocol
  TSQLite3HttpClientWinSock = class(TSQLite3HttpClientGeneric)
  protected
    /// internal HTTP/1.1 compatible client
    fSocket: THttpClientSocket;
    /// call fSocket.Request()
    function InternalRequest(const url, method: RawUTF8;
      var Header, Data, DataType: RawUTF8): Int64Rec; override;
  public
    /// internal HTTP/1.1 compatible client
    property Socket: THttpClientSocket read fSocket;
    /// connect to TSQLite3HttpServer on aServer:aPort
    constructor Create(const aServer, aPort: AnsiString; aModel: TSQLModel); reintroduce;
    /// release all memory, internal SQLite3 client and HTTP handlers
    destructor Destroy; override;
  end;

{$ifdef USEWININET}
  /// HTTP/1.1 RESTFUL JSON SQLite3 Client abstract class using either WinINet
  // either TWinHTTP API
  // - not to be called directly, but via TSQLite3HttpClientWinINet or (even
  // better) TSQLite3HttpClientWinHTTP overriden classes 
  TSQLite3HttpClientWinGeneric = class(TSQLite3HttpClientGeneric)
  protected
    fWinAPI: TWinHttpAPI;
    fServer, fPort, fProxyName, fProxyByPass: AnsiString;
    fHttps: boolean;
    /// overriden methods will create either a TWinINet, either a TWinHTTP instance
    procedure InternalCreate; virtual; abstract;
    /// call fWinAPI.Request()
    function InternalRequest(const url, method: RawUTF8;
      var Header, Data, DataType: RawUTF8): Int64Rec; override;
  public
    /// connect to TSQLite3HttpServer on aServer:aPort
    // - optional aProxyName may contain the name of the proxy server to use,
    // and aProxyByPass an optional semicolon delimited list of host names or
    // IP addresses, or both, that should not be routed through the proxy
    constructor Create(const aServer, aPort: AnsiString; aModel: TSQLModel;
      aHttps: boolean=false; const aProxyName: AnsiString='';
      const aProxyByPass: AnsiString=''); reintroduce;
    /// release all memory, internal SQLite3 client and HTTP handlers
    destructor Destroy; override;
    /// internal class instance used for the connection
    // - will return either a TWinINet, either a TWinHTTP class instance
    property WinAPI: TWinHttpAPI read fWinAPI;
  end;

  /// HTTP/1.1 RESTFUL JSON SQLite3 Client class using WinINet API
  // - this class is 15/20 times slower than TSQLite3HttpClient using SynCrtSock
  // on a local machine, but was found to be faster throughout local networks
  // - this class is able to connect via the secure HTTPS protocol
  // - it will retrieve by default the Internet Explorer proxy settings, and 
  // display some error messages or authentification dialog on screen
  // - you can optionaly specify manual Proxy settings at constructor level
  // - by design, the WinINet API should not be used from a service
  // - is implemented by creating a TWinINet internal class instance
  TSQLite3HttpClientWinINet = class(TSQLite3HttpClientWinGeneric)
  protected
    procedure InternalCreate; override;
  end;

  {{ HTTP/1.1 RESTFUL JSON SQLite3 Client class using WinHTTP API
   - has a common behavior as THttpClientSocket() but seems to be faster
     over a network and is able to retrieve the current proxy settings
     (if available) and handle secure HTTPS connection - so it seems to be used
     in your client programs: TSQLite3HttpClient will therefore map to this class
   - WinHTTP does not share directly any proxy settings with Internet Explorer.
     The default WinHTTP proxy configuration is set by either
     proxycfg.exe on Windows XP and Windows Server 2003 or earlier, either
     netsh.exe on Windows Vista and Windows Server 2008 or later; for instance,
     you can run "proxycfg -u" or "netsh winhttp import proxy source=ie" to use
     the current user's proxy settings for Internet Explorer (under 64 bit
     Vista/Seven, to configure applications using the 32 bit WinHttp settings,
     call netsh or proxycfg bits from %SystemRoot%\SysWOW64 folder explicitely)
   - you can optionaly specify manual Proxy settings at constructor level
   - by design, the WinHTTP API can be used from a service or a server
   - is implemented by creating a TWinHTTP internal class instance }
  TSQLite3HttpClientWinHTTP = class(TSQLite3HttpClientWinGeneric)
  protected
    procedure InternalCreate; override;
  end;

  /// HTTP/1.1 RESTFUL JSON SQLite3 default Client class
  // - under Windows, map the TSQLite3HttpClientWinHTTP class 
  TSQLite3HttpClient = TSQLite3HttpClientWinHTTP;
{$else}
  /// HTTP/1.1 RESTFUL JSON SQLite3 Client class 

  TSQLite3HttpClient = TSQLite3HttpClientWinSock;
{$endif}


implementation


{ TSQLite3HttpClientGeneric }

function TSQLite3HttpClientGeneric.InternalURI(const url, method: RawUTF8;
  Resp, Head, SendData: PRawUTF8): Int64Rec;
var Headers, Content, ContentType: RawUTF8;
    P: PUTF8Char;
{$ifdef WITHLOG}
    Log: ISynLog;
{$endif}
begin
................................................................................
    Head^ := Headers;
{$ifdef WITHLOG}
  Log.Log(sllClient,'% % result.Lo=% .Hi=%',[method,url,result.Lo,result.Hi],self);
{$endif}
end;


{ TSQLite3HttpClientWinSock }

constructor TSQLite3HttpClientWinSock.Create(const aServer, aPort: AnsiString;
  aModel: TSQLModel);
begin
  inherited Create(aModel);
  fSocket := THttpClientSocket.Open(aServer,aPort);
{$ifdef USETCPPREFIX}
  fSocket.TCPPrefix := 'magic';
{$endif}
................................................................................
   fSocket.RegisterCompress(CompressSynLZ);
{$endif}
{$ifdef COMPRESSDEFLATE}
   fSocket.RegisterCompress(CompressDeflate);
{$endif}
end;

destructor TSQLite3HttpClientWinSock.Destroy;
begin
  fSocket.Free;
  inherited Destroy;
end;

function TSQLite3HttpClientWinSock.InternalRequest(const url, method: RawUTF8;
  var Header, Data, DataType: RawUTF8): Int64Rec;
begin
  result.Lo := fSocket.Request(url,method,KeepAliveMS,Header,Data,DataType,false);
  result.Hi := GetCardinal(pointer(fSocket.HeaderValue('Server-InternalState')));
  Header := fSocket.HeaderGetText;
  Data := fSocket.Content;
end;


{$ifdef USEWININET}

{ TSQLite3HttpClientWinGeneric }

constructor TSQLite3HttpClientWinGeneric.Create(const aServer, aPort: AnsiString;
  aModel: TSQLModel; aHttps: boolean; const aProxyName, aProxyByPass: AnsiString);
begin
  inherited Create(aModel);
  fServer := aServer;
  fPort := aPort;
  fHttps := aHttps;
  fProxyName := aProxyName;
................................................................................
  fWinAPI.RegisterCompress(CompressSynLZ);
{$endif}
{$ifdef COMPRESSDEFLATE}
   fWinAPI.RegisterCompress(CompressDeflate);
{$endif}
end;

destructor TSQLite3HttpClientWinGeneric.Destroy;
begin
  inherited;
  fWinAPI.Free;
end;

function TSQLite3HttpClientWinGeneric.InternalRequest(const url, method: RawUTF8;
  var Header, Data, DataType: RawUTF8): Int64Rec;
var OutHeader, OutData: RawByteString;
begin
  if fWinAPI=nil then
    result.Lo := 404 else begin
    try
      result.Lo := fWinAPI.Request(url,method,KeepAliveMS,Header,Data,DataType,
................................................................................
      FindIniNameValue(pointer(OutHeader),'SERVER-INTERNALSTATE: ')));
    Header := OutHeader;
    Data := OutData;
  end;
end;


{ TSQLite3HttpClientWinINet }

procedure TSQLite3HttpClientWinINet.InternalCreate;
begin
  fWinAPI := TWinINet.Create(fServer,fPort,fHttps,fProxyName,fProxyByPass);
end;


{ TSQLite3HttpClientWinHTTP }

procedure TSQLite3HttpClientWinHTTP.InternalCreate;
begin
  fWinAPI := TWinHTTP.Create(fServer,fPort,fHttps,fProxyName,fProxyByPass);
end;

{$endif}

end.
|

|
|







 







|







 







|
|







 







>
>
>
>







 







|


|
|













|




|



|









|

|




|

|
|
|










|






|






|
|







|




|



|











|




|
|
|

|
>
|






|

|







 







|

|







 







|





|











|

|







 







|





|







 







|

|





|

|







1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
...
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
...
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
...
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
/// HTTP/1.1 RESTFUL JSON Client classes for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotHttpClient;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****



      HTTP/1.1 RESTFUL JSON Client for mORMot
    ******************************************

   - use internaly the JSON format for content communication
   - can be called by any JSON-aware AJAX application
   - can optionaly compress the returned data to optimize Internet bandwidth
   - speed is very high: more than 20MB/sec R/W localy on a 1.8GHz Sempron,
     i.e. 400Mb/sec of duplex raw IP data, with about 200 µs only elapsed
     by request (direct call is 50 µs, so bottle neck is the Win32 API),
................................................................................
      - fixed GPF issue at closing
      - fixed unnecessary dual URL signing (when authentication actived)

    Version 1.17
      - added optional aProxyName, aProxyByPass parameters to
        TSQLite3HttpClientWinGeneric / TSQLite3HttpClientWinINet and
        TSQLite3HttpClientWinHTTP constructors
		
    Version 1.18
	   - unit SQLite3HttpClient.pas renamed mORMotHttpClient.pas
	   - classes TSQLite3HttpClient* renamed as TSQLHttpClient*

}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
................................................................................
  SynZip,
{$endif}
{$ifdef COMPRESSSYNLZ}
  SynLZ,
{$endif}
  SynCrtSock,
  SynCommons,
  mORMot;

type
  /// Generic HTTP/1.1 RESTFUL JSON mORMot Client class
  TSQLHttpClientGeneric = class(TSQLRestClientURI)
  protected
    /// process low-level HTTP/1.1 request
    // - call by URI() public method
    // - returns 200,202,204 if OK, http status error otherwize in result.Lo
    // - returns Server-InternalState in result.Hi
    function InternalRequest(const url, method: RawUTF8;
      var Header, Data, DataType: RawUTF8): Int64Rec; virtual; abstract;
    /// method calling the RESTful server fServer via HTTP/1.1
    // - calls the InternalRequest() protected method
    function InternalURI(const url, method: RawUTF8; Resp: PRawUTF8=nil;
      Head: PRawUTF8=nil; SendData: PRawUTF8=nil): Int64Rec; override;
  public
    /// the time (in milliseconds) to keep the connection alive with the
    // TSQLHttpServer
    // - default is 20000, i.e. 20 seconds
    KeepAliveMS: cardinal;
  end;

  /// HTTP/1.1 RESTFUL JSON mORMot Client class using SynCrtSock / WinSock
  // - will give the best performance on a local computer, but has been found
  // out to be slower over a network
  // - is not able to use secure HTTPS protocol
  TSQLHttpClientWinSock = class(TSQLHttpClientGeneric)
  protected
    /// internal HTTP/1.1 compatible client
    fSocket: THttpClientSocket;
    /// call fSocket.Request()
    function InternalRequest(const url, method: RawUTF8;
      var Header, Data, DataType: RawUTF8): Int64Rec; override;
  public
    /// internal HTTP/1.1 compatible client
    property Socket: THttpClientSocket read fSocket;
    /// connect to TSQLHttpServer on aServer:aPort
    constructor Create(const aServer, aPort: AnsiString; aModel: TSQLModel); reintroduce;
    /// release all memory, internal RESTful client and HTTP handlers
    destructor Destroy; override;
  end;

{$ifdef USEWININET}
  /// HTTP/1.1 RESTFUL JSON mORMot Client abstract class using either WinINet
  // either TWinHTTP API
  // - not to be called directly, but via TSQLHttpClientWinINet or (even
  // better) TSQLHttpClientWinHTTP overriden classes 
  TSQLHttpClientWinGeneric = class(TSQLHttpClientGeneric)
  protected
    fWinAPI: TWinHttpAPI;
    fServer, fPort, fProxyName, fProxyByPass: AnsiString;
    fHttps: boolean;
    /// overriden methods will create either a TWinINet, either a TWinHTTP instance
    procedure InternalCreate; virtual; abstract;
    /// call fWinAPI.Request()
    function InternalRequest(const url, method: RawUTF8;
      var Header, Data, DataType: RawUTF8): Int64Rec; override;
  public
    /// connect to TSQLHttpServer on aServer:aPort
    // - optional aProxyName may contain the name of the proxy server to use,
    // and aProxyByPass an optional semicolon delimited list of host names or
    // IP addresses, or both, that should not be routed through the proxy
    constructor Create(const aServer, aPort: AnsiString; aModel: TSQLModel;
      aHttps: boolean=false; const aProxyName: AnsiString='';
      const aProxyByPass: AnsiString=''); reintroduce;
    /// release all memory, internal RESTful client and HTTP handlers
    destructor Destroy; override;
    /// internal class instance used for the connection
    // - will return either a TWinINet, either a TWinHTTP class instance
    property WinAPI: TWinHttpAPI read fWinAPI;
  end;

  /// HTTP/1.1 RESTFUL JSON mORMot Client class using WinINet API
  // - this class is 15/20 times slower than TSQLHttpClient using SynCrtSock
  // on a local machine, but was found to be faster throughout local networks
  // - this class is able to connect via the secure HTTPS protocol
  // - it will retrieve by default the Internet Explorer proxy settings, and 
  // display some error messages or authentification dialog on screen
  // - you can optionaly specify manual Proxy settings at constructor level
  // - by design, the WinINet API should not be used from a service
  // - is implemented by creating a TWinINet internal class instance
  TSQLHttpClientWinINet = class(TSQLHttpClientWinGeneric)
  protected
    procedure InternalCreate; override;
  end;

  {{ HTTP/1.1 RESTFUL JSON Client class using WinHTTP API
   - has a common behavior as THttpClientSocket() but seems to be faster
     over a network and is able to retrieve the current proxy settings
     (if available) and handle secure HTTPS connection - so it seems to be used
     in your client programs: TSQLHttpClient will therefore map to this class
   - WinHTTP does not share directly any proxy settings with Internet Explorer.
     The default WinHTTP proxy configuration is set by either
     proxycfg.exe on Windows XP and Windows Server 2003 or earlier, either
     netsh.exe on Windows Vista and Windows Server 2008 or later; for instance,
     you can run "proxycfg -u" or "netsh winhttp import proxy source=ie" to use
     the current user's proxy settings for Internet Explorer (under 64 bit
     Vista/Seven, to configure applications using the 32 bit WinHttp settings,
     call netsh or proxycfg bits from %SystemRoot%\SysWOW64 folder explicitely)
   - you can optionaly specify manual Proxy settings at constructor level
   - by design, the WinHTTP API can be used from a service or a server
   - is implemented by creating a TWinHTTP internal class instance }
  TSQLHttpClientWinHTTP = class(TSQLHttpClientWinGeneric)
  protected
    procedure InternalCreate; override;
  end;

  /// HTTP/1.1 RESTFUL JSON default mORMot Client class
  // - under Windows, map the TSQLHttpClientWinHTTP class 
  TSQLHttpClient = TSQLHttpClientWinHTTP;
{$else}
  /// HTTP/1.1 RESTFUL JSON deault mORMot Client class 
  // - not unders Windows, map the WinSock implementation class
  TSQLHttpClient = TSQLHttpClientWinSock;
{$endif}


implementation


{ TSQLHttpClientGeneric }

function TSQLHttpClientGeneric.InternalURI(const url, method: RawUTF8;
  Resp, Head, SendData: PRawUTF8): Int64Rec;
var Headers, Content, ContentType: RawUTF8;
    P: PUTF8Char;
{$ifdef WITHLOG}
    Log: ISynLog;
{$endif}
begin
................................................................................
    Head^ := Headers;
{$ifdef WITHLOG}
  Log.Log(sllClient,'% % result.Lo=% .Hi=%',[method,url,result.Lo,result.Hi],self);
{$endif}
end;


{ TSQLHttpClientWinSock }

constructor TSQLHttpClientWinSock.Create(const aServer, aPort: AnsiString;
  aModel: TSQLModel);
begin
  inherited Create(aModel);
  fSocket := THttpClientSocket.Open(aServer,aPort);
{$ifdef USETCPPREFIX}
  fSocket.TCPPrefix := 'magic';
{$endif}
................................................................................
   fSocket.RegisterCompress(CompressSynLZ);
{$endif}
{$ifdef COMPRESSDEFLATE}
   fSocket.RegisterCompress(CompressDeflate);
{$endif}
end;

destructor TSQLHttpClientWinSock.Destroy;
begin
  fSocket.Free;
  inherited Destroy;
end;

function TSQLHttpClientWinSock.InternalRequest(const url, method: RawUTF8;
  var Header, Data, DataType: RawUTF8): Int64Rec;
begin
  result.Lo := fSocket.Request(url,method,KeepAliveMS,Header,Data,DataType,false);
  result.Hi := GetCardinal(pointer(fSocket.HeaderValue('Server-InternalState')));
  Header := fSocket.HeaderGetText;
  Data := fSocket.Content;
end;


{$ifdef USEWININET}

{ TSQLHttpClientWinGeneric }

constructor TSQLHttpClientWinGeneric.Create(const aServer, aPort: AnsiString;
  aModel: TSQLModel; aHttps: boolean; const aProxyName, aProxyByPass: AnsiString);
begin
  inherited Create(aModel);
  fServer := aServer;
  fPort := aPort;
  fHttps := aHttps;
  fProxyName := aProxyName;
................................................................................
  fWinAPI.RegisterCompress(CompressSynLZ);
{$endif}
{$ifdef COMPRESSDEFLATE}
   fWinAPI.RegisterCompress(CompressDeflate);
{$endif}
end;

destructor TSQLHttpClientWinGeneric.Destroy;
begin
  inherited;
  fWinAPI.Free;
end;

function TSQLHttpClientWinGeneric.InternalRequest(const url, method: RawUTF8;
  var Header, Data, DataType: RawUTF8): Int64Rec;
var OutHeader, OutData: RawByteString;
begin
  if fWinAPI=nil then
    result.Lo := 404 else begin
    try
      result.Lo := fWinAPI.Request(url,method,KeepAliveMS,Header,Data,DataType,
................................................................................
      FindIniNameValue(pointer(OutHeader),'SERVER-INTERNALSTATE: ')));
    Header := OutHeader;
    Data := OutData;
  end;
end;


{ TSQLHttpClientWinINet }

procedure TSQLHttpClientWinINet.InternalCreate;
begin
  fWinAPI := TWinINet.Create(fServer,fPort,fHttps,fProxyName,fProxyByPass);
end;


{ TSQLHttpClientWinHTTP }

procedure TSQLHttpClientWinHTTP.InternalCreate;
begin
  fWinAPI := TWinHTTP.Create(fServer,fPort,fHttps,fProxyName,fProxyByPass);
end;

{$endif}

end.

Name change from SQLite3/SQLite3HttpServer.pas to SQLite3/mORMotHttpServer.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116


117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
...
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
/// HTTP/1.1 RESTFUL JSON mORMot Server classes
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3HttpServer;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


     HTTP/1.1 RESTFUL JSON Client/Server using SQLite3 database engine
    *******************************************************************

   - use internaly the JSON format for content communication
   - can be called by TSQLite3HttpClient class from direct Delphi clients
     (see SQLite3HttpClient unit)
   - can be called by any JSON-aware AJAX application
   - can optionaly compress the returned data to optimize Internet bandwidth
   - speed is very high: more than 20MB/sec R/W localy on a 1.8GHz Sempron,
     i.e. 400Mb/sec of duplex raw IP data, with about 200 µs only elapsed
     by request (direct call is 50 µs, so bottle neck is the Win32 API),
     i.e. 5000 requests per second, with 113 result rows (i.e. 4803 bytes
     of JSON data each)... try to find a faster JSON HTTP server! ;)
................................................................................
        some space

    Version 1.8
      - includes Unitary Testing class and functions
      - allows content-type changing for GET blob fields

    Version 1.12
      - TSQLite3HttpServer now handle multiple TSQLRestServer instances
        per one server (dispatching requests via the Root URI used)
      - new AddServer method, to register a TSQLRestServer after launch
      - new TSQLRestServer.OnlyJSONRequests property

    Version 1.13
      - can now use fast http.sys kernel-mode server (THttpApiServer) if
        available, and our pure Delphi THttpServer on default
      - now can compress its content using deflate or faster SynLZ algorithm
      - by default, will handle SynLZ compression for TSQLite3HttpClient
      - can make TCP/IP stream not HTTP compliant (for security visions)
      - TSQLite3HttpServer.Request now uses the new THttpServerGeneric.Request
        virtual abstract method prototype to handle THttpApiServer

    Version 1.16
      - added optional aRestAccessRights parameter in TSQLite3HttpServer.AddServer
        to override the default HTTP_DEFAULT_ACCESS_RIGHTS settings
      - added ServerThreadPoolCount parameter to TSQLite3HttpServer.Create()
        constructor, set by default to 32 - will speed up process of slow
        requests (e.g. a POST with some huge data transmitted at slow rate)
      - fixed error in case of URI similar to 'root?session_signature=...'
      - fixed incorect thread count in TSQLite3HttpServer.Create
      - regression tests are now extracted from this unit, in order to allow
        construction of a TSQLite3HTTPServer instance without the need
        of linking the SQLite3 engine to the executable

    Version 1.17
      - made URI check case-insensitive (as for official RFC)
      - TSQLite3HttpServer will now call virtual TSQLRestServer.EndCurrentThread
        method in each of its terminating threads, to release any thread-specific
        resources (for instance, external connections in SQlite3DB)

    Version 1.18


      - added TSQLite3HttpServer.RemoveServer() method
      - TSQLite3HttpServer now handles sub-domains generic matching (via
        TSQLModel.URIMatch call) at database model level (e.g. you can set
        root='/root/sub1' URIs)

}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
  - SynLZ is much faster than deflate/zip, so is preferred
  - can be set global for Client and Server applications
  - with SynLZ, the 440 KB JSON for TTestClientServerAccess._TSQLite3HttpClient
    is compressed into 106 KB with no speed penalty (it's even a bit faster)
    whereas deflate, even with its level set to 1 (fastest), is 25 % slower
  - is defined by default for a Delphi client }

{.$define COMPRESSDEFLATE}
{ if defined, will use deflate/zip for content compression
  - can be set global for Client and Server applications
................................................................................
  SynZip,
{$endif}
{$ifdef COMPRESSSYNLZ}
  SynLZ,
{$endif}
  SynCrtSock,
  SynCommons,
  SQLite3Commons;


const
  /// the default access rights used by the HTTP server if none is specified
  HTTP_DEFAULT_ACCESS_RIGHTS: PSQLAccessRights = @SUPERVISOR_ACCESS_RIGHTS;

type
  /// HTTP/1.1 RESTFUL JSON SQLite3 Server class
  // - this server is multi-threaded and not blocking
  // - will first try to use fastest http.sys kernel-mode server (i.e. create a
  // THttpApiServer instance); it should work OK under XP or WS 2K3 - but
  // you need to have administrator rights under Vista or Seven: if http.sys
  // fails to initialize, it will use a pure Delphi THttpServer instance; a
  // solution is to call the THttpApiServer.AddUrlAuthorize class method during
  // program setup for the desired port, in order to allow it for every user
  // - just create it and it will serve SQL statements as UTF-8 JSON
  // - for a true AJAX server, expanded data is prefered - your code may contain:
  // ! DBServer.NoAJAXJSON := false;
  TSQLite3HttpServer = class
  protected
    fOnlyJSONRequests: boolean;
    fHttpServer: THttpServerGeneric;
    fPort, fDomainName: AnsiString;
    /// internal servers to compute responses
    fDBServers: array of record
      Server: TSQLRestServer;
................................................................................
    // instance must handle a particular request from its URI
    // - port is an AnsiString, as expected by the WinSock API
    // - aDomainName is the URLprefix to be used for HttpAddUrl API call:
    // it could be either a fully qualified case-insensitive domain name
    // an IPv4 or IPv6 literal string, or a wildcard ('+' will bound
    // to all domain names for the specified port, '*' will accept the request
    // when no other listening hostnames match the request for that port) - this
    // parameter is ignored by the TSQLite3HttpApiServer instance
    // - if DontUseHttpApiServer is set, kernel-mode HTTP.SYS server won't be used
    // and standard Delphi code will be called instead (not recommended)
    // - by default, the PSQLAccessRights will be set to nil
    // - the ServerThreadPoolCount parameter will set the number of threads
    // to be initialized to handle incoming connections (default is 32, which
    // may be sufficient for most cases, maximum is 256)
    constructor Create(const aPort: AnsiString;
................................................................................
    // - specify one TSQLRestServer server class to be used
    // - port is an AnsiString, as expected by the WinSock API
    // - aDomainName is the URLprefix to be used for HttpAddUrl API call
    constructor Create(const aPort: AnsiString; aServer: TSQLRestServer;
      const aDomainName: AnsiString='+';
      DontUseHttpApiServer: Boolean=false; aRestAccessRights: PSQLAccessRights=nil;
      ServerThreadPoolCount: Integer=32); reintroduce; overload;
    /// release all memory, internal SQLite3 server (if any) and HTTP handlers
    destructor Destroy; override;
    /// try to register another TSQLRestServer instance to the HTTP server
    // - each TSQLRestServer class must have an unique Model.Root value, to
    // identify which instance must handle a particular request from its URI
    // - an optional aRestAccessRights parameter is available to override the
    // default HTTP_DEFAULT_ACCESS_RIGHTS access right setting - but you shall
    // better rely on the authentication feature included in the framework
................................................................................
    property OnlyJSONRequests: boolean read fOnlyJSONRequests write fOnlyJSONRequests;
  end;


implementation


{ TSQLite3HttpServer }

function TSQLite3HttpServer.AddServer(aServer: TSQLRestServer;
  aRestAccessRights: PSQLAccessRights): boolean;
var i, n: integer;
{$ifdef WITHLOG}
    Log: ISynLog;
{$endif}
begin
  result := False;
................................................................................
  {$ifdef WITHLOG}
  finally
    Log.Log(sllDebug,'result=% for Root=%',[JSON_BOOLEAN[Result],aServer.Model.Root]);
  end;
  {$endif}
end;

function TSQLite3HttpServer.RemoveServer(aServer: TSQLRestServer): boolean;
var i,j,n: integer;
{$ifdef WITHLOG}
    Log: ISynLog;
{$endif}
begin
  result := False;
{$ifdef WITHLOG}
................................................................................
  {$ifdef WITHLOG}
  finally
    Log.Log(sllDebug,'result=% for Root=%',[JSON_BOOLEAN[Result],aServer.Model.Root]);
  end;
  {$endif}
end;

constructor TSQLite3HttpServer.Create(const aPort: AnsiString;
  const aServers: array of TSQLRestServer; const aDomainName: AnsiString;
  DontUseHttpApiServer: Boolean; ServerThreadPoolCount: Integer);
var i,j: integer;
    ErrMsg: string;
{$ifdef WITHLOG}
    Log: ISynLog;
{$endif}
................................................................................
    if ServerThreadPoolCount>1 then
      THttpApiServer(fHttpServer).Clone(ServerThreadPoolCount-1);
{$ifdef WITHLOG}
  Log.Log(sllInfo,'% initialized',[fHttpServer],self);
{$endif}
end;

constructor TSQLite3HttpServer.Create(const aPort: AnsiString;
  aServer: TSQLRestServer; const aDomainName: AnsiString;
  DontUseHttpApiServer: boolean; aRestAccessRights: PSQLAccessRights;
  ServerThreadPoolCount: integer);
begin
  Create(aPort,[aServer],aDomainName,DontUseHttpApiServer,ServerThreadPoolCount);
  if aRestAccessRights<>nil then
    DBServerAccessRight[0] := aRestAccessRights;
end;

destructor TSQLite3HttpServer.Destroy;
begin
  fHttpServer.Free;
  inherited;
end;

function TSQLite3HttpServer.GetDBServer(Index: Integer): TSQLRestServer;
begin
  if (Self<>nil) and (cardinal(Index)<cardinal(length(fDBServers))) then
    result := fDBServers[Index].Server else
    result := nil;
end;

function TSQLite3HttpServer.GetDBServerCount: integer;
begin
  result := length(fDBServers);
end;

procedure TSQLite3HttpServer.SetDBServerAccessRight(Index: integer;
  Value: PSQLAccessRights);
begin
  if Value=nil then
    Value := HTTP_DEFAULT_ACCESS_RIGHTS;
  if (Self<>nil) and (cardinal(Index)<cardinal(length(fDBServers))) then
    fDBServers[Index].RestAccessRights := Value;
end;

function TSQLite3HttpServer.Request(
  const InURL, InMethod, InHeaders, InContent, InContentType: RawByteString;
    out OutContent, OutContentType, OutCustomHeader: RawByteString): cardinal;
var URL, Head: RawUTF8;
    i: integer;
    P: PUTF8Char;
begin
  if (InURL='') or (InMethod='') or
................................................................................
        Head := Trim(Trim(Head)+#13#10'Server-InternalState: '+Int32ToUtf8(Hi));
        OutCustomHeader := Head;
        break;
      end;
  end;
end;

procedure TSQLite3HttpServer.HttpThreadTerminate(Sender: TObject);
var i: integer;
begin
  if self<>nil then
    for i := 0 to high(fDBServers) do
      fDBServers[i].Server.EndCurrentThread(self);
end;

procedure TSQLite3HttpServer.HttpThreadStart(Sender: TThread);
var i: integer;
begin
  if self<>nil then
    for i := 0 to high(fDBServers) do
      fDBServers[i].Server.BeginCurrentThread(Sender);
end;


end.
|


|







 







|







 







|
|


|
|







 







|








|

|



|

|



|

|




|




>
>
|
|











|







 







|







|










|







 







|







 







|







 







|

|







 







|







 







|







 







|









|





|






|




|








|







 







|







|









1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
...
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
...
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
...
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
/// HTTP/1.1 RESTFUL JSON Server classes for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotHttpServer;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


     HTTP/1.1 RESTFUL JSON Client/Server for mORMot
    ************************************************

   - use internaly the JSON format for content communication
   - can be called by TSQLHttpClient class from direct Delphi clients
     (see mORMotHttpClient unit)
   - can be called by any JSON-aware AJAX application
   - can optionaly compress the returned data to optimize Internet bandwidth
   - speed is very high: more than 20MB/sec R/W localy on a 1.8GHz Sempron,
     i.e. 400Mb/sec of duplex raw IP data, with about 200 µs only elapsed
     by request (direct call is 50 µs, so bottle neck is the Win32 API),
     i.e. 5000 requests per second, with 113 result rows (i.e. 4803 bytes
     of JSON data each)... try to find a faster JSON HTTP server! ;)
................................................................................
        some space

    Version 1.8
      - includes Unitary Testing class and functions
      - allows content-type changing for GET blob fields

    Version 1.12
      - TSQLHttpServer now handle multiple TSQLRestServer instances
        per one server (dispatching requests via the Root URI used)
      - new AddServer method, to register a TSQLRestServer after launch
      - new TSQLRestServer.OnlyJSONRequests property

    Version 1.13
      - can now use fast http.sys kernel-mode server (THttpApiServer) if
        available, and our pure Delphi THttpServer on default
      - now can compress its content using deflate or faster SynLZ algorithm
      - by default, will handle SynLZ compression for TSQLHttpClient
      - can make TCP/IP stream not HTTP compliant (for security visions)
      - TSQLHttpServer.Request now uses the new THttpServerGeneric.Request
        virtual abstract method prototype to handle THttpApiServer

    Version 1.16
      - added optional aRestAccessRights parameter in TSQLHttpServer.AddServer
        to override the default HTTP_DEFAULT_ACCESS_RIGHTS settings
      - added ServerThreadPoolCount parameter to TSQLHttpServer.Create()
        constructor, set by default to 32 - will speed up process of slow
        requests (e.g. a POST with some huge data transmitted at slow rate)
      - fixed error in case of URI similar to 'root?session_signature=...'
      - fixed incorect thread count in TSQLHttpServer.Create
      - regression tests are now extracted from this unit, in order to allow
        construction of a TSQLHttpServer instance without the need
        of linking the SQLite3 engine to the executable

    Version 1.17
      - made URI check case-insensitive (as for official RFC)
      - TSQLHttpServer will now call virtual TSQLRestServer.EndCurrentThread
        method in each of its terminating threads, to release any thread-specific
        resources (for instance, external connections in SQlite3DB)

    Version 1.18
	  - renamed SQLite3HttpServer.pas unit to mORMotHttpServer.pas
	  - classes TSQLHttpServer* renamed as TSQLHttpServer*
      - added TSQLHttpServer.RemoveServer() method
      - TSQLHttpServer now handles sub-domains generic matching (via
        TSQLModel.URIMatch call) at database model level (e.g. you can set
        root='/root/sub1' URIs)

}

interface

{$define COMPRESSSYNLZ}
{ if defined, will use SynLZ for content compression
  - SynLZ is much faster than deflate/zip, so is preferred
  - can be set global for Client and Server applications
  - with SynLZ, the 440 KB JSON for TTestClientServerAccess._TSQLHttpClient
    is compressed into 106 KB with no speed penalty (it's even a bit faster)
    whereas deflate, even with its level set to 1 (fastest), is 25 % slower
  - is defined by default for a Delphi client }

{.$define COMPRESSDEFLATE}
{ if defined, will use deflate/zip for content compression
  - can be set global for Client and Server applications
................................................................................
  SynZip,
{$endif}
{$ifdef COMPRESSSYNLZ}
  SynLZ,
{$endif}
  SynCrtSock,
  SynCommons,
  mORMot;


const
  /// the default access rights used by the HTTP server if none is specified
  HTTP_DEFAULT_ACCESS_RIGHTS: PSQLAccessRights = @SUPERVISOR_ACCESS_RIGHTS;

type
  /// HTTP/1.1 RESTFUL JSON mORMot Server class
  // - this server is multi-threaded and not blocking
  // - will first try to use fastest http.sys kernel-mode server (i.e. create a
  // THttpApiServer instance); it should work OK under XP or WS 2K3 - but
  // you need to have administrator rights under Vista or Seven: if http.sys
  // fails to initialize, it will use a pure Delphi THttpServer instance; a
  // solution is to call the THttpApiServer.AddUrlAuthorize class method during
  // program setup for the desired port, in order to allow it for every user
  // - just create it and it will serve SQL statements as UTF-8 JSON
  // - for a true AJAX server, expanded data is prefered - your code may contain:
  // ! DBServer.NoAJAXJSON := false;
  TSQLHttpServer = class
  protected
    fOnlyJSONRequests: boolean;
    fHttpServer: THttpServerGeneric;
    fPort, fDomainName: AnsiString;
    /// internal servers to compute responses
    fDBServers: array of record
      Server: TSQLRestServer;
................................................................................
    // instance must handle a particular request from its URI
    // - port is an AnsiString, as expected by the WinSock API
    // - aDomainName is the URLprefix to be used for HttpAddUrl API call:
    // it could be either a fully qualified case-insensitive domain name
    // an IPv4 or IPv6 literal string, or a wildcard ('+' will bound
    // to all domain names for the specified port, '*' will accept the request
    // when no other listening hostnames match the request for that port) - this
    // parameter is ignored by the TSQLHttpApiServer instance
    // - if DontUseHttpApiServer is set, kernel-mode HTTP.SYS server won't be used
    // and standard Delphi code will be called instead (not recommended)
    // - by default, the PSQLAccessRights will be set to nil
    // - the ServerThreadPoolCount parameter will set the number of threads
    // to be initialized to handle incoming connections (default is 32, which
    // may be sufficient for most cases, maximum is 256)
    constructor Create(const aPort: AnsiString;
................................................................................
    // - specify one TSQLRestServer server class to be used
    // - port is an AnsiString, as expected by the WinSock API
    // - aDomainName is the URLprefix to be used for HttpAddUrl API call
    constructor Create(const aPort: AnsiString; aServer: TSQLRestServer;
      const aDomainName: AnsiString='+';
      DontUseHttpApiServer: Boolean=false; aRestAccessRights: PSQLAccessRights=nil;
      ServerThreadPoolCount: Integer=32); reintroduce; overload;
    /// release all memory, internal mORMot server and HTTP handlers
    destructor Destroy; override;
    /// try to register another TSQLRestServer instance to the HTTP server
    // - each TSQLRestServer class must have an unique Model.Root value, to
    // identify which instance must handle a particular request from its URI
    // - an optional aRestAccessRights parameter is available to override the
    // default HTTP_DEFAULT_ACCESS_RIGHTS access right setting - but you shall
    // better rely on the authentication feature included in the framework
................................................................................
    property OnlyJSONRequests: boolean read fOnlyJSONRequests write fOnlyJSONRequests;
  end;


implementation


{ TSQLHttpServer }

function TSQLHttpServer.AddServer(aServer: TSQLRestServer;
  aRestAccessRights: PSQLAccessRights): boolean;
var i, n: integer;
{$ifdef WITHLOG}
    Log: ISynLog;
{$endif}
begin
  result := False;
................................................................................
  {$ifdef WITHLOG}
  finally
    Log.Log(sllDebug,'result=% for Root=%',[JSON_BOOLEAN[Result],aServer.Model.Root]);
  end;
  {$endif}
end;

function TSQLHttpServer.RemoveServer(aServer: TSQLRestServer): boolean;
var i,j,n: integer;
{$ifdef WITHLOG}
    Log: ISynLog;
{$endif}
begin
  result := False;
{$ifdef WITHLOG}
................................................................................
  {$ifdef WITHLOG}
  finally
    Log.Log(sllDebug,'result=% for Root=%',[JSON_BOOLEAN[Result],aServer.Model.Root]);
  end;
  {$endif}
end;

constructor TSQLHttpServer.Create(const aPort: AnsiString;
  const aServers: array of TSQLRestServer; const aDomainName: AnsiString;
  DontUseHttpApiServer: Boolean; ServerThreadPoolCount: Integer);
var i,j: integer;
    ErrMsg: string;
{$ifdef WITHLOG}
    Log: ISynLog;
{$endif}
................................................................................
    if ServerThreadPoolCount>1 then
      THttpApiServer(fHttpServer).Clone(ServerThreadPoolCount-1);
{$ifdef WITHLOG}
  Log.Log(sllInfo,'% initialized',[fHttpServer],self);
{$endif}
end;

constructor TSQLHttpServer.Create(const aPort: AnsiString;
  aServer: TSQLRestServer; const aDomainName: AnsiString;
  DontUseHttpApiServer: boolean; aRestAccessRights: PSQLAccessRights;
  ServerThreadPoolCount: integer);
begin
  Create(aPort,[aServer],aDomainName,DontUseHttpApiServer,ServerThreadPoolCount);
  if aRestAccessRights<>nil then
    DBServerAccessRight[0] := aRestAccessRights;
end;

destructor TSQLHttpServer.Destroy;
begin
  fHttpServer.Free;
  inherited;
end;

function TSQLHttpServer.GetDBServer(Index: Integer): TSQLRestServer;
begin
  if (Self<>nil) and (cardinal(Index)<cardinal(length(fDBServers))) then
    result := fDBServers[Index].Server else
    result := nil;
end;

function TSQLHttpServer.GetDBServerCount: integer;
begin
  result := length(fDBServers);
end;

procedure TSQLHttpServer.SetDBServerAccessRight(Index: integer;
  Value: PSQLAccessRights);
begin
  if Value=nil then
    Value := HTTP_DEFAULT_ACCESS_RIGHTS;
  if (Self<>nil) and (cardinal(Index)<cardinal(length(fDBServers))) then
    fDBServers[Index].RestAccessRights := Value;
end;

function TSQLHttpServer.Request(
  const InURL, InMethod, InHeaders, InContent, InContentType: RawByteString;
    out OutContent, OutContentType, OutCustomHeader: RawByteString): cardinal;
var URL, Head: RawUTF8;
    i: integer;
    P: PUTF8Char;
begin
  if (InURL='') or (InMethod='') or
................................................................................
        Head := Trim(Trim(Head)+#13#10'Server-InternalState: '+Int32ToUtf8(Hi));
        OutCustomHeader := Head;
        break;
      end;
  end;
end;

procedure TSQLHttpServer.HttpThreadTerminate(Sender: TObject);
var i: integer;
begin
  if self<>nil then
    for i := 0 to high(fDBServers) do
      fDBServers[i].Server.EndCurrentThread(self);
end;

procedure TSQLHttpServer.HttpThreadStart(Sender: TThread);
var i: integer;
begin
  if self<>nil then
    for i := 0 to high(fDBServers) do
      fDBServers[i].Server.BeginCurrentThread(Sender);
end;


end.

Name change from SQLite3/SQLite3Pages.pas to SQLite3/mORMotReport.pas.

1
2
3
4
5
6
7
8
9
10
11
..
19
20
21
22
23
24
25
26
27
28
29


30
31
32
33
34
35
36
..
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
..
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
173
174
175
176
177
178
179

180
181
182
183
184
185
186
/// Reporting unit
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3Pages;

(*
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

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

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.



  Contributor(s):
  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
................................................................................

  ***** END LICENSE BLOCK *****


    Initial Notes and Copyright:
   ******************************

 Component Name:  TGDIPages
 Module:          Pages
 Description:     Report writer and previewer
 Version:         1.6
 Date:            25-MAY-2004 (initial version)
 Target:          Win32, Delphi 3 - Delphi 7
 Author:          Angus Johnson,   angusj-AT-myrealbox-DOT-com
 Copyright        © 2003 Angus Johnson

 Notes:
   * TGDIPages is designed as a simple lightweight report writer. Reports are
     created in code, they are not banded, nor are they directly linked to
     TDatasets. If you're looking for a dataset aware report writer then
     TGDIPages is not for you. TGDIPages is a visual component based on a
................................................................................
         > left, right and justified in non-columned text
         > left, right and currency in columned text
     + Tabs to Assigned tabstops
     + Multi-line page headers, footers and column headers
     + Multiple fonts can be used.
     + Angled text output
     + Single, line & half, and double line spacing
     + Methods for printing bitmaps, lines, boxes and arrows
     + Page numbering can be redefined
     + Text output 'groups' prevent blocks of text spanning across pages
     + Designed around a TScrollbox descendant preview window with:
       mouse click zoom control; keyboard handling of lineup, linedown,
       pageup and pagedown srolling; mouse wheel scrolling.

   * In order to get the best print quality, TGDIPages uses the selected
................................................................................


  Enhanced for the freeware Synopse framework:
 **********************************************

  - Windows XP, Vista and Seven compatibility adding
  - fix printing metafiles and page groups on some printer (with bad drivers)
  - optionnaly use antialiaised drawing (by using GDI+)
  - popup menu creation, with zoom, print or copy features (and custom entries)
  - direct PDF export (if a PDF printer is installed, e.g. free doPDF)
  - direct page export to clipboard as text
  - optional Black and White / Duplex mode (with out TPrinterNew custom class)
  - new usefull methods for easy text adding (especially column definition)
  - new fast double buffering drawing
  - limited Unicode charset conversion
  - speed up and various bug fixes

  Modifications © 2009-2010 Arnaud Bouchez

  Version 1.4 - February 8, 2010
  - whole Synopse SQLite3 database framework released under the GNU Lesser
    General Public License version 3, instead of generic "Public Domain"

  Version 1.6
  - new version, using our SynGdiPlus unit: if the GDI+ is available,
................................................................................

  Version 1.17
  - now whole text process is UNICODE-ready, even on pre-Delphi-2009 versions
  - now implements font fall-back in internal Anti-Aliaised drawing,
    if the new ForceInternalAntiAliasedFontFallBack property is set to TRUE

  Version 1.18

  - fixed issue about disabled Zoom menu entry if no Outline is defined


*)

interface




|







 







|

|

>
>







 







|





|







 







|







 







|

|




|
|

|







 







>







1
2
3
4
5
6
7
8
9
10
11
..
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/// Reporting unit
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotReport;

(*
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

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

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse framework.

  The Initial Developer of the Original Code is Angus Johnson.

  Portions created by the Initial Developer are Copyright (C) 2003
  the Initial Developer. All Rights Reserved.
  Portions created by Arnaud Bouchez for Synopse are Copyright (C) 2012
  Arnaud Bouchez. All Rights Reserved.

  Contributor(s):
  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
................................................................................

  ***** END LICENSE BLOCK *****


    Initial Notes and Copyright:
   ******************************

 Component Name:  TPages
 Module:          Pages
 Description:     Report writer and previewer
 Version:         1.6
 Date:            25-MAY-2004 (initial version)
 Target:          Win32, Delphi 3 - Delphi 7
 Author:          Angus Johnson, http://www.angusj.com
 Copyright        © 2003 Angus Johnson

 Notes:
   * TGDIPages is designed as a simple lightweight report writer. Reports are
     created in code, they are not banded, nor are they directly linked to
     TDatasets. If you're looking for a dataset aware report writer then
     TGDIPages is not for you. TGDIPages is a visual component based on a
................................................................................
         > left, right and justified in non-columned text
         > left, right and currency in columned text
     + Tabs to Assigned tabstops
     + Multi-line page headers, footers and column headers
     + Multiple fonts can be used.
     + Angled text output
     + Single, line & half, and double line spacing
     + Methods for printing bitmaps, metafiles, lines, boxes and arrows
     + Page numbering can be redefined
     + Text output 'groups' prevent blocks of text spanning across pages
     + Designed around a TScrollbox descendant preview window with:
       mouse click zoom control; keyboard handling of lineup, linedown,
       pageup and pagedown srolling; mouse wheel scrolling.

   * In order to get the best print quality, TGDIPages uses the selected
................................................................................


  Enhanced for the freeware Synopse framework:
 **********************************************

  - Windows XP, Vista and Seven compatibility adding
  - fix printing metafiles and page groups on some printer (with bad drivers)
  - optionnaly use antialiaised drawing (via SynGdiPlus unit)
  - popup menu creation, with zoom, print or copy features (and custom entries)
  - direct PDF export (if a PDF printer is installed, or via SynPdf unit)
  - direct page export to clipboard as text
  - optional Black and White / Duplex mode (with out TPrinterNew custom class)
  - new usefull methods for easy text adding (especially column definition)
  - new fast double buffering drawing
  - full Unicode text process (even before Delphi 2009)
  - speed up and various bug fixes to work with Delphi 5 up to XE3

  Modifications © 2009-2012 Arnaud Bouchez

  Version 1.4 - February 8, 2010
  - whole Synopse SQLite3 database framework released under the GNU Lesser
    General Public License version 3, instead of generic "Public Domain"

  Version 1.6
  - new version, using our SynGdiPlus unit: if the GDI+ is available,
................................................................................

  Version 1.17
  - now whole text process is UNICODE-ready, even on pre-Delphi-2009 versions
  - now implements font fall-back in internal Anti-Aliaised drawing,
    if the new ForceInternalAntiAliasedFontFallBack property is set to TRUE

  Version 1.18
  - renamed SQLite3Pages.pas to mORMotReport.pas
  - fixed issue about disabled Zoom menu entry if no Outline is defined


*)

interface

Name change from SQLite3/SQLite3.pas to SQLite3/mORMotSQLite3.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
197
198
199
200
201
202
203

204
205
206
207
208
209
210
...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/// SQLite3 embedded Database engine used as the kernel of mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    Version 1.17
    - updated SQLite3 engine to version 3.7.14
    - added overriden TSQLRestServerDB.FlushInternalDBCache method
    - added TSQLRestServerDB.BackupGZ method for live database backup into a
      compressed .gz archive file

    Version 1.18

    - updated SQLite3 engine to version 3.7.14.1
    - fixed potential GPF issue in TSQLRestServerDB.Destroy when registered
      TSQLVVirtualtableModuleDBs are already destroyed
    - extraction of TTestSQLite3Engine code into SynSelfTests.pas unit

}

................................................................................
  Classes,
{$ifndef LVCL}
  Contnrs,
{$endif}
  SynZip,
  SynCommons,
  SynSQLite3,
  SQLite3Commons;

{$define INCLUDE_FTS3}
{ define this if you want to include the FTS3/FTS4 feature into the library
  - FTS3 is an SQLite module implementing full-text search
  - will include also FTS4 extension module since 3.7.4
  - see http://www.sqlite.org/fts3.html for documentation
  - is defined by default, but can be unset to save about 50 KB of code size
  - should be defined for both SynSQLite3 and SQLite3 units }

{.$define USEFASTCALL}
{ use the fastcall calling convention to access the SQLite3 library
  - BCC32 -pr fastcall (=Delphi resgister) is buggy, don't know why
   (because of issues with BCC32 itself, or some obfuscated calls in source?)
  - should be defined for both SynSQLite3 and SQLite3 units }


{ ****************** SQLite3 database used as kernel of our mORMot framework } 

type
  /// Execute a SQL statement in the local SQLite3 database engine, and get
  // result in memory
|


|







 







|







 







>







 







|







|





|







1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
...
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/// SQLite3 embedded Database engine used as the mORMOt SQL kernel
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotSQLite3;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    Version 1.17
    - updated SQLite3 engine to version 3.7.14
    - added overriden TSQLRestServerDB.FlushInternalDBCache method
    - added TSQLRestServerDB.BackupGZ method for live database backup into a
      compressed .gz archive file

    Version 1.18
	- unit SQLite3.pas renamed mORMotSQLite3.pas
    - updated SQLite3 engine to version 3.7.14.1
    - fixed potential GPF issue in TSQLRestServerDB.Destroy when registered
      TSQLVVirtualtableModuleDBs are already destroyed
    - extraction of TTestSQLite3Engine code into SynSelfTests.pas unit

}

................................................................................
  Classes,
{$ifndef LVCL}
  Contnrs,
{$endif}
  SynZip,
  SynCommons,
  SynSQLite3,
  mORMot;

{$define INCLUDE_FTS3}
{ define this if you want to include the FTS3/FTS4 feature into the library
  - FTS3 is an SQLite module implementing full-text search
  - will include also FTS4 extension module since 3.7.4
  - see http://www.sqlite.org/fts3.html for documentation
  - is defined by default, but can be unset to save about 50 KB of code size
  - should be defined for both SynSQLite3 and mORMotSQLite3 units }

{.$define USEFASTCALL}
{ use the fastcall calling convention to access the SQLite3 library
  - BCC32 -pr fastcall (=Delphi resgister) is buggy, don't know why
   (because of issues with BCC32 itself, or some obfuscated calls in source?)
  - should be defined for both SynSQLite3 and mORMotSQLite3 units }


{ ****************** SQLite3 database used as kernel of our mORMot framework } 

type
  /// Execute a SQL statement in the local SQLite3 database engine, and get
  // result in memory

Name change from SQLite3/SQLite3SelfTests.pas to SQLite3/mORMotSelfTests.pas.

1
2
3
4
5
6
7
8
9
10
11
..
51
52
53
54
55
56
57

58
59
60
61
62
63
64
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/// automated tests for common units of the Synopse mORMot Framework
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3SelfTests;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
    background IDE compilers (used e.g. for syntax checking) was not able
    to compile the tests within the main .dpr source code

  Version 1.17
  - fixed LVCL and Delphi 5 compilation issues

  Version 1.18

  - added TInterfaceStub and TInterfaceMock classes testing

}

interface

{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64
................................................................................
  SynCrypto,
  SynCrtSock,
  SynCommons,
  SynSQLite3,
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}
{$ifndef FPC}
  SQLite3Commons,
  SynBigTable,
  SQLite3,
  SQLite3FastCgiServer,
  SQLite3HttpClient,
  SQLite3HttpServer,
  SQLite3Service,
  //SQLite3BigTable,
{$endif FPC}
{$endif CPU64}
{$endif DELPHI5OROLDER}
{$ifndef FPC}
{$ifndef LVCL}
  SynPdf,          
  Contnrs,
................................................................................
  SynOleDB,
  SynDBOracle,
  SynDBODBC,
  SynDBSQLite3,
{$ifndef FPC}
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}
  SQLite3DB,
{$endif CPU64}
{$endif DELPHI5OROLDER}
{$endif FPC}
{$endif LVCL}
  //SynDBODBC,
{$endif FPC}
  SynZip,
................................................................................
{$ifndef LVCL}
    , TTestSynopsePDF
{$endif}
{$endif} ]);
end;

{$ifdef FPC}
type // SQLite3Commons unit doesn't compile with FPC yet
  TSQLLog = TSynLog;
{$else}
{$ifdef CPU64}
type // SQLite3Commons unit doesn't compile with Delphi 64 bit yet
  TSQLLog = TSynLog;
{$else}
{$ifdef DELPHI5OROLDER}
type // SQLite3Commons unit doesn't compile with Delphi 5 yet
  TSQLLog = TSynLog;
{$else}
procedure TTestSynopsemORMotFramework._mORMot;
begin
  AddCase([TTestBasicClasses,TTestFileBased,TTestFileBasedWAL,TTestMemoryBased]);
  AddCase([TTestClientServerAccess]);
  AddCase([TTestServiceOrientedArchitecture]);



|







 







>







 







|

|
|
|
|
|
|







 







|







 







|



|



|







1
2
3
4
5
6
7
8
9
10
11
..
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
..
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/// automated tests for common units of the Synopse mORMot Framework
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotSelfTests;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
    background IDE compilers (used e.g. for syntax checking) was not able
    to compile the tests within the main .dpr source code

  Version 1.17
  - fixed LVCL and Delphi 5 compilation issues

  Version 1.18
  - renamed SQLite3SelfTests.pas to mORMotSelfTests.pas
  - added TInterfaceStub and TInterfaceMock classes testing

}

interface

{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64
................................................................................
  SynCrypto,
  SynCrtSock,
  SynCommons,
  SynSQLite3,
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}
{$ifndef FPC}
  mORMot,
  SynBigTable,
  mORMotSQLite3,
  mORMotFastCgiServer,
  mORMotHttpClient,
  mORMotHttpServer,
  mORMotService,
  //mORMotBigTable,
{$endif FPC}
{$endif CPU64}
{$endif DELPHI5OROLDER}
{$ifndef FPC}
{$ifndef LVCL}
  SynPdf,          
  Contnrs,
................................................................................
  SynOleDB,
  SynDBOracle,
  SynDBODBC,
  SynDBSQLite3,
{$ifndef FPC}
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}
  mORMotDB,
{$endif CPU64}
{$endif DELPHI5OROLDER}
{$endif FPC}
{$endif LVCL}
  //SynDBODBC,
{$endif FPC}
  SynZip,
................................................................................
{$ifndef LVCL}
    , TTestSynopsePDF
{$endif}
{$endif} ]);
end;

{$ifdef FPC}
type // mORMot.pas unit doesn't compile with FPC yet
  TSQLLog = TSynLog;
{$else}
{$ifdef CPU64}
type // mORMot.pas unit doesn't compile with Delphi 64 bit yet
  TSQLLog = TSynLog;
{$else}
{$ifdef DELPHI5OROLDER}
type // mORMot.pas unit doesn't compile with Delphi 5 yet
  TSQLLog = TSynLog;
{$else}
procedure TTestSynopsemORMotFramework._mORMot;
begin
  AddCase([TTestBasicClasses,TTestFileBased,TTestFileBasedWAL,TTestMemoryBased]);
  AddCase([TTestClientServerAccess]);
  AddCase([TTestServiceOrientedArchitecture]);

Name change from SQLite3/SQLite3Service.pas to SQLite3/mORMotService.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
54
55
56
57
58
59
60



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
...
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
/// Win NT Service managment classes
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SQLite3Service;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    Version 1.4 - February 8, 2010
    - whole Synopse SQLite3 database framework released under the GNU Lesser
      General Public License version 3, instead of generic "Public Domain"

    Version 1.16
    - code refactoring after Leander007 proposals for better compatibility -
      see http://synopse.info/forum/viewtopic.php?id=584




}

interface

{$I Synopse.inc}

{$ifdef LVCL}
  {$define NOSQLITE3COMMONS}
  // define it if you don't need to include the SQLite3Commons unit
  // - could save some code size in the final executable
{$endif}

uses
  Windows, WinSVC, Messages, Classes, SysUtils, SynCommons
  {$ifndef NOSQLITE3COMMONS}, SQLite3Commons{$endif} // translated caption needed only if with full UI
  ;

const
  CM_SERVICE_CONTROL_CODE = WM_USER+1000;

type
  {{ all possible states of the service }
................................................................................
    SERVICE_CONTINUE_PENDING: result := ssResuming;
    SERVICE_PAUSE_PENDING:    result := ssPausing;
    SERVICE_PAUSED:           result := ssPaused;
    else result := ssNotInstalled; // e.g. SERVICE_CONTROL_SHUTDOWN
  end;
end;

{$ifdef NOSQLITE3COMMONS} // translated caption needed only if with full UI
function ServiceStateText(State: TServiceState): string;
var P: PShortString;
begin
  P := GetEnumName(TypeInfo(TServiceState),ord(State));
  result := string(copy(P^,3,length(P^)-2));
end;
{$else}
|

|
|







 







|







 







>
>
>








|
|





|







 







|







1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
/// Win NT Service managment classes for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORmotService;

{
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    Version 1.4 - February 8, 2010
    - whole Synopse SQLite3 database framework released under the GNU Lesser
      General Public License version 3, instead of generic "Public Domain"

    Version 1.16
    - code refactoring after Leander007 proposals for better compatibility -
      see http://synopse.info/forum/viewtopic.php?id=584
	  
	Version 1.18
	- renamed SQLite3Service.pas to mORMotService.pas

}

interface

{$I Synopse.inc}

{$ifdef LVCL}
  {$define NOMORMOTKERNEL}
  // define it if you don't need to include the mORmot.pas unit
  // - could save some code size in the final executable
{$endif}

uses
  Windows, WinSVC, Messages, Classes, SysUtils, SynCommons
  {$ifndef NOMORMOTKERNEL}, mORmot{$endif} // translated caption needed only if with full UI
  ;

const
  CM_SERVICE_CONTROL_CODE = WM_USER+1000;

type
  {{ all possible states of the service }
................................................................................
    SERVICE_CONTINUE_PENDING: result := ssResuming;
    SERVICE_PAUSE_PENDING:    result := ssPausing;
    SERVICE_PAUSED:           result := ssPaused;
    else result := ssNotInstalled; // e.g. SERVICE_CONTROL_SHUTDOWN
  end;
end;

{$ifdef NOMORMOTKERNEL} // translated caption needed only if with full UI
function ServiceStateText(State: TServiceState): string;
var P: PShortString;
begin
  P := GetEnumName(TypeInfo(TServiceState),ord(State));
  result := string(copy(P^,3,length(P^)-2));
end;
{$else}

Name change from SQLite3/SQLite3ToolBar.pas to SQLite3/mORMotToolBar.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


20
21
22

23


24










25
26








27

28
29
30
31
32
33
34
35
36
37
..
73
74
75
76
77
78
79

80
81
82
83
84
85
86
..
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/// Database-driven Office 2007 Toolbar
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3ToolBar;

interface

{

    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.



    This library is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

    GNU Lesser General Public License for more details.













    You should have received a copy of the GNU Lesser General Public License
    along with this library. If not, see <http://www.gnu.org/licenses/>.












        Handle a Toolbar for Database actions
       ***************************************

      - full Office 2007 Ribbon menus are created directly from code
      - every database table has its own Ribbon tab
      - every Ribbon tab has its own buttons, corresponding to actions, as
        defined in the code in a custom enumeration type

................................................................................
    Version 1.15
    - TSQLRibbon.AddToReport method can work with self=nil

    Version 1.16
    - includes new TSynAnsiConvert classes for handling Ansi charsets

    Version 1.18

    - introducing TSQLPropInfo* classes to decouple ORM definitions from RTTI

}

uses
  Windows, Consts, Dialogs, ShellAPI,
  SysUtils, Forms, Classes, Messages, Graphics,
................................................................................
  AdvOfficePager, AdvToolBar, AdvGlowButton, AdvMenus, AdvShapeButton, AdvPreviewMenu,
  AdvToolBarStylers, AdvPreviewMenuStylers, AdvOfficePagerStylers,
  AdvOfficeStatusBarStylers, AdvPanel,
  TaskDialog, TaskDialogEx, GDIPicture,
{$else}
  StdCtrls, ComCtrls, SynTaskDialog, Buttons, CommCtrl,
{$endif USETMSPACK}
  SynCommons, SQLite3Commons,
  SQLite3UI, SQlite3i18n, SQLite3UILogin,
  SQLite3Pages, SynGdiPlus, SynZip;


type
  /// used to mark which shortcut keys have already been affected
  TFreeShortCutSet = set of ord('A')..ord('Z');

  /// a simple object to get one char shortcuts from caption value
|


|










|
|
<
<

>
>
|
|
<
>
|
>
>

>
>
>
>
>
>
>
>
>
>
|
<
>
>
>
>
>
>
>
>

>


|







 







>







 







|
|
<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
/// ORM-driven Office 2007 Toolbar for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotToolBar;

interface

{

    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1



  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL


  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),

  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
  use your version of this file under the terms of the MPL, indicate your
  decision by deleting the provisions above and replace them with the notice
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


        Handle a Toolbar for CRUD/ORM actions
       ***************************************

      - full Office 2007 Ribbon menus are created directly from code
      - every database table has its own Ribbon tab
      - every Ribbon tab has its own buttons, corresponding to actions, as
        defined in the code in a custom enumeration type

................................................................................
    Version 1.15
    - TSQLRibbon.AddToReport method can work with self=nil

    Version 1.16
    - includes new TSynAnsiConvert classes for handling Ansi charsets

    Version 1.18
	- renamed SQLite3ToolBar.pas to mORMotToolBar.pas
    - introducing TSQLPropInfo* classes to decouple ORM definitions from RTTI

}

uses
  Windows, Consts, Dialogs, ShellAPI,
  SysUtils, Forms, Classes, Messages, Graphics,
................................................................................
  AdvOfficePager, AdvToolBar, AdvGlowButton, AdvMenus, AdvShapeButton, AdvPreviewMenu,
  AdvToolBarStylers, AdvPreviewMenuStylers, AdvOfficePagerStylers,
  AdvOfficeStatusBarStylers, AdvPanel,
  TaskDialog, TaskDialogEx, GDIPicture,
{$else}
  StdCtrls, ComCtrls, SynTaskDialog, Buttons, CommCtrl,
{$endif USETMSPACK}
  SynCommons, SynGdiPlus, SynZip,
  mORMot, mORMotReport, mORMotUI, mORMoti18n, mORMotUILogin;



type
  /// used to mark which shortcut keys have already been affected
  TFreeShortCutSet = set of ord('A')..ord('Z');

  /// a simple object to get one char shortcuts from caption value

Name change from SQLite3/SQLite3UI.pas to SQLite3/mORMotUI.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
...
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
/// Grid to display Database content
// - this unit is a part of the freeware Synopse SQLite3 database framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3UI;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3 database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    - includes new TSynAnsiConvert classes for handling Ansi charsets

    Version 1.17
    -  global functions AddApplicationToFirewall() and AddPortToFirewall()
       are now compatible with Windows XP, Vista and Seven - renamed on purpose 

    Version 1.18

    - introducing TSQLPropInfo* classes to decouple ORM definitions from RTTI

*)


interface

{$I Synopse.inc}

uses
  Windows,
  SynCommons, SQLite3Commons, SQLite3i18n,
  SysUtils, Classes, Messages, Variants,
  {$ifdef WITHUXTHEME}
  Themes,
  {$endif}
  Graphics, StdCtrls, Controls, Grids, Buttons, ExtCtrls, Forms;

type
................................................................................
    // - you can specify here some additional text to be displayed when the mouse
    // is hover the component
    property AdditionalHint: string read FAdditionalHint write FAdditionalHint;
  end;


/// register the TSynIntegerLabeledEdit component in the IDE toolbar
// - not necessary for the SQLite3 framework to run: since all User Interface
// is created from code, and not from the Delphi IDE, you don't have to register
// anything
procedure Register;


resourcestring
  SErrorFieldNotValid = 'Field "%s"'#13'does not contain a valid %s value';
  SErrorFieldTooSmall = 'Field "%s"'#13'is too small, value must be >= %s';
  SErrorFieldTooLarge = 'Field "%s"'#13'is too large, value must be <= %s';
................................................................................
procedure AddPortToFirewall(const EntryName: string; PortNumber: cardinal);


/// fill TStringGrid.Cells[] with the supplied data
// - will be slower than the TSQLTableToGrid method, but will work on
// a non standard TDrawGrid component
// - it will display date & time and enumerates as plain text, and handle
// the header properly (using the current SQLite3i18n language settings, if any)
// - the Client optional parameter will be used to display any RecordRef column
// - all data will be stored within the TStringGrid: you can safely release the
// Source data after having called this procedure  
procedure FillStringGrid(Source: TSQLTable; Dest: TStringGrid; Client: TSQLRest=nil);


implementation
|
|

|







 







|







 







>











|







 







|

|







 







|







1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
...
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
/// Grid to display database content for mORMot
// - this unit is a part of the freeware mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotUI;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    - includes new TSynAnsiConvert classes for handling Ansi charsets

    Version 1.17
    -  global functions AddApplicationToFirewall() and AddPortToFirewall()
       are now compatible with Windows XP, Vista and Seven - renamed on purpose 

    Version 1.18
	- renamed SQLite3UI.pas to mORMotUI.pas
    - introducing TSQLPropInfo* classes to decouple ORM definitions from RTTI

*)


interface

{$I Synopse.inc}

uses
  Windows,
  SynCommons, mORMot, mORMoti18n,
  SysUtils, Classes, Messages, Variants,
  {$ifdef WITHUXTHEME}
  Themes,
  {$endif}
  Graphics, StdCtrls, Controls, Grids, Buttons, ExtCtrls, Forms;

type
................................................................................
    // - you can specify here some additional text to be displayed when the mouse
    // is hover the component
    property AdditionalHint: string read FAdditionalHint write FAdditionalHint;
  end;


/// register the TSynIntegerLabeledEdit component in the IDE toolbar
// - not necessary for the mORMot framework to run: since all User Interface
// is created from code, and not from the Delphi IDE, you don't have to register
// anything unless you define your own forms including those components
procedure Register;


resourcestring
  SErrorFieldNotValid = 'Field "%s"'#13'does not contain a valid %s value';
  SErrorFieldTooSmall = 'Field "%s"'#13'is too small, value must be >= %s';
  SErrorFieldTooLarge = 'Field "%s"'#13'is too large, value must be <= %s';
................................................................................
procedure AddPortToFirewall(const EntryName: string; PortNumber: cardinal);


/// fill TStringGrid.Cells[] with the supplied data
// - will be slower than the TSQLTableToGrid method, but will work on
// a non standard TDrawGrid component
// - it will display date & time and enumerates as plain text, and handle
// the header properly (using the current mORMoti18n.pas language settings, if any)
// - the Client optional parameter will be used to display any RecordRef column
// - all data will be stored within the TStringGrid: you can safely release the
// Source data after having called this procedure  
procedure FillStringGrid(Source: TSQLTable; Dest: TStringGrid; Client: TSQLRest=nil);


implementation

Name change from SQLite3/SQLite3UIEdit.dfm to SQLite3/mORMotUIEdit.dfm.

Name change from SQLite3/SQLite3UIEdit.pas to SQLite3/mORMotUIEdit.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
82
83
84
85
86
87
88

89
90
91
92
93
94
95
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/// Record edition dialog, used to edit record content on the screen
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3UIEdit;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3 database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  - handle TCreateTime published property / sftCreateTime SQL field

  Version 1.16
  - moved OnComponentCreated event call after all component initialization
    (allow adding paired components on purpose)

  Version 1.18

  - introducing TSQLPropInfo* classes in order to decouple ORM definitions from
    the underlying RTTI


*)


................................................................................
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
{$ifdef USETMSPACK}
  TaskDialog,
{$endif}
  SynCommons, SQLite3Commons,
  SQLite3UILogin, SQLite3UI, SQLite3i18n, SQLite3ToolBar,
  SynTaskDialog, StdCtrls, ExtCtrls, ImgList, ComCtrls;

type
  /// Event used to customize the input component after creation
  TOnComponentCreated = procedure(Obj: TObject; Prop: TSQLPropInfo; Comp: TWinControl) of object;
  /// Event used for the window creation
  TOnComponentCreate = function(Obj: TObject; Prop: TSQLPropInfo; Parent: TWinControl): TWinControl of object;
|


|







 







|







 







>







 







|
<







1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
..
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
/// Record edition dialog, used to edit record content with mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotUIEdit;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  - handle TCreateTime published property / sftCreateTime SQL field

  Version 1.16
  - moved OnComponentCreated event call after all component initialization
    (allow adding paired components on purpose)

  Version 1.18
  - renamed SQLite3UIEdit.pas to mORMotUIEdit.pas
  - introducing TSQLPropInfo* classes in order to decouple ORM definitions from
    the underlying RTTI


*)


................................................................................
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
{$ifdef USETMSPACK}
  TaskDialog,
{$endif}
  SynCommons, mORMot, mORMotUILogin, mORMotUI, mORMoti18n, mORMotToolBar,

  SynTaskDialog, StdCtrls, ExtCtrls, ImgList, ComCtrls;

type
  /// Event used to customize the input component after creation
  TOnComponentCreated = procedure(Obj: TObject; Prop: TSQLPropInfo; Comp: TWinControl) of object;
  /// Event used for the window creation
  TOnComponentCreate = function(Obj: TObject; Prop: TSQLPropInfo; Parent: TWinControl): TWinControl of object;

Name change from SQLite3/SQLite3UILogin.dfm to SQLite3/mORMotUILogin.dfm.

Name change from SQLite3/SQLite3UILogin.pas to SQLite3/mORMotUILogin.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
66
67
68
69
70
71
72



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/// some common User Interface functions and dialogs
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SQLite3UILogin;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3 database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................

    Version 1.15
    - new InputBox global function
    - new QueryMasked parameter to display * in InputBox/InputQuery editor field

    Version 1.16
    - InputBox function will now focus the input field component by default




*)

interface

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

uses
  Windows, Messages, SysUtils, Classes, Graphics, Consts,
  Controls, Forms, StdCtrls, ExtCtrls, Buttons,
{$ifdef USETMSPACK}
  AdvGlowButton, TaskDialog, TaskDialogEx, AdvToolBarStylers, AdvToolBar,
{$endif USETMSPACK}
  SynTaskDialog, SynGdiPlus, SynCommons, SQLite3UI, SQLite3Commons;


type
  /// Form used to Log User and enter its password
  TLoginForm = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
|

|
|







 







|







 







>
>
>













|







1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/// some common User Interface functions and dialogs for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotUILogin;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................

    Version 1.15
    - new InputBox global function
    - new QueryMasked parameter to display * in InputBox/InputQuery editor field

    Version 1.16
    - InputBox function will now focus the input field component by default
	
	Version 1.18
	- renamed SQLite3UILogin.pas to mORMotUILogin.pas

*)

interface

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

uses
  Windows, Messages, SysUtils, Classes, Graphics, Consts,
  Controls, Forms, StdCtrls, ExtCtrls, Buttons,
{$ifdef USETMSPACK}
  AdvGlowButton, TaskDialog, TaskDialogEx, AdvToolBarStylers, AdvToolBar,
{$endif USETMSPACK}
  SynTaskDialog, SynGdiPlus, SynCommons, mORMot, mORMotUI;


type
  /// Form used to Log User and enter its password
  TLoginForm = class(TForm)
    Label1: TLabel;
    Label2: TLabel;

Name change from SQLite3/SQLite3UIOptions.dfm to SQLite3/mORMotUIOptions.dfm.

Name change from SQLite3/SQLite3UIOptions.pas to SQLite3/mORMotUIOptions.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
57
58
59
60
61
62
63



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/// General Options setting dialog
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SQLite3UIOptions;

(*
    This file is part of Synopse SQLite3 database framework.

    Synopse SQLite3 database framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3 database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    - minor fixes and enhancements

    Version 1.13
    - Delphi 2009/2010/XE compatibility fixes

    Version 1.15
    - Get rid of TMS components dependency




*)

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  SynCommons, SQLite3Commons,
  SQLite3UILogin, SQLite3UI, SQLite3UIEdit, SQLite3ToolBar, SQLite3i18n,
{$ifdef USETMSPACK}
  TaskDialog,
{$endif}
  StdCtrls, ExtCtrls, ComCtrls, SynTaskDialog;

type
  /// Options setting dialog
|

|
|


|

|







 







|







 







>
>
>







|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/// General Options setting dialog for mORmot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotUIOptions;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    - minor fixes and enhancements

    Version 1.13
    - Delphi 2009/2010/XE compatibility fixes

    Version 1.15
    - Get rid of TMS components dependency
	
	Version 1.18
	- renamed SQLite3UIOptions.pas to mORMotUIOptions.pas

*)

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  SynCommons, 
  mORMot, mORMotUILogin, mORMotUI, mORMotUIEdit, mORMotToolBar, mORMoti18n,
{$ifdef USETMSPACK}
  TaskDialog,
{$endif}
  StdCtrls, ExtCtrls, ComCtrls, SynTaskDialog;

type
  /// Options setting dialog

Name change from SQLite3/SQLite3UIQuery.dfm to SQLite3/mORMotUIQuery.dfm.

Name change from SQLite3/SQLite3UIQuery.pas to SQLite3/mORMotUIQuery.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
53
54
55
56
57
58
59



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/// Form handling queries to a User Interface Grid
// - this unit is a part of the freeware Synopse SQLite3 database framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.15
unit SQLite3UIQuery;

(*
    This file is part of Synopse SQLite3 database framework.

    Synopse SQLite3 database framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3 database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  - TSQLQueryEvent() usage has therefore been modified for custom queries

  Version 1.15
  - now use TMS component pack only if USETMSPACK global conditional is defined:
    by default, will use only VCL components (i.e. TSynButton) for the form
  - handle TModTime published property / sftModTime SQL field
  - handle TCreateTime published property / sftCreateTime SQL field




*)

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  SynCommons, SynTaskDialog,
  SQLite3Commons, SQLite3UI, SQLite3UILogin, SQLite3i18n,
  StdCtrls, ComCtrls;

type
  /// this Form perform simple Visual queries to a Grid
  // - mark or unmark items, depending of the input of the User on this form
  // - use TSQLRest.QueryIsTrue() method for standard fields and parameters
  // - use TSQLQueryCustom records previously registered to the TSQLRest class,
|
|
|
|


|

|







 







|







 







>
>
>








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/// Form handling queries to a User Interface Grid for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMotUIQuery;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  - TSQLQueryEvent() usage has therefore been modified for custom queries

  Version 1.15
  - now use TMS component pack only if USETMSPACK global conditional is defined:
    by default, will use only VCL components (i.e. TSynButton) for the form
  - handle TModTime published property / sftModTime SQL field
  - handle TCreateTime published property / sftCreateTime SQL field
  
  Version 1.18
  - renamed SQLite3UIQuery.pas to mORMotUIQuery.pas

*)

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  SynCommons, SynTaskDialog,
  mORMot, mORMotUI, mORMotUILogin, mORMoti18n,
  StdCtrls, ComCtrls;

type
  /// this Form perform simple Visual queries to a Grid
  // - mark or unmark items, depending of the input of the User on this form
  // - use TSQLRest.QueryIsTrue() method for standard fields and parameters
  // - use TSQLQueryCustom records previously registered to the TSQLRest class,

Name change from SQLite3/SQLite3VCL.pas to SQLite3/mORMotVCL.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
41
42
43
44
45
46
47
48


49
50
51
52
53
54
55
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/// DB VCL dataset using TSQLTable/TSQLTableJSON data access
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SQLite3VCL;

{
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****

  Version 1.17
  - first public release, corresponding to Synopse mORMot Framework 1.17




}

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

interface

................................................................................
  {$ifdef ISDELPHIXE2}System.SysUtils,{$else}SysUtils,{$endif}
  Classes,
  Contnrs,
{$ifndef DELPHI5OROLDER}
  Variants,
  MidasLib,
{$endif}
  SynCommons,
  SQLite3Commons,
  DB, DBClient;

/// convert a TSQLTable result into a VCL DataSet
// - current implementation will return a TClientDataSet instance, created from
// the supplied TSQLTable content (a more optimized version may appear later)
// - for better speed with Delphi older than Delphi 2009 Update 3, it is
// recommended to use http://andy.jgknet.de/blog/bugfix-units/midas-speed-fix-12


|
|


|

|







 







|
>
>







 







|
<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
/// DB VCL dataset using TSQLTable/TSQLTableJSON data access
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORmotVCL;

{
    This file is part of Synopse mORmot framework.

    Synopse mORmot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****

  Version 1.17
  - first public release, corresponding to Synopse mORMot Framework 1.17
  
  Version 1.18
  - renamed SQLite3VCL.pas to mORMotVCL.pas

}

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

interface

................................................................................
  {$ifdef ISDELPHIXE2}System.SysUtils,{$else}SysUtils,{$endif}
  Classes,
  Contnrs,
{$ifndef DELPHI5OROLDER}
  Variants,
  MidasLib,
{$endif}
  SynCommons, mORmot,

  DB, DBClient;

/// convert a TSQLTable result into a VCL DataSet
// - current implementation will return a TClientDataSet instance, created from
// the supplied TSQLTable content (a more optimized version may appear later)
// - for better speed with Delphi older than Delphi 2009 Update 3, it is
// recommended to use http://andy.jgknet.de/blog/bugfix-units/midas-speed-fix-12

Name change from SQLite3/SQLite3i18n.pas to SQLite3/mORMoti18n.pas.

1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
160
161
162
163
164
165
166

167
168
169
170
171
172
173
...
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
...
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
...
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
....
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
/// internationalization (i18n) routines and classes
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SQLite3i18n;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3 database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


    i18n routines for the Synopse SQLite3 database framework
   **********************************************************

   - works internaly with the string type, that is AnsiString with code pages
     and charsets for compiler versions earlier than Delphi 2009, and
     UnicodeString since Delphi 2009 and up -> so it's 100% VCL compatible
   - can load language definition files encoded in Unicode or UTF8
   - auto-call SetThreadLocale() for full application i18n
   - update default locale settings values (date,currency..)
................................................................................
    - now Iso2S() - i.e. overriden i18nDateText global function pointer - will
      handle a date-only or time-only supplied value as expected

    Version 1.17
    - some refactoring about process in-memory code patching

    Version 1.18

    - introducing TSQLPropInfo* classes to decouple ORM definitions from RTTI


*)

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

................................................................................
// must be set globally for the whole application

{.$define ENHANCEDRTL}
{ define this if you DID install our Enhanced Runtime library: it has already
  hacked the "legacy" LoadResString() procedure and added a LoadResStringTranslate()
  - it will be unset automaticaly (see below) for Delphi 2009 and up, since
    no version of our Enhanced RTL exists for these compilers 
  - this conditional must be defined in both SQLite3Commons and SQLite3i18n units,
    or (even better) globally in the Project options }

{.$define USESHARP}
// if defined, $$,$$$,$$$ are replaced with some globals in _()

{$ifndef NOI18N}
  // with this global define, you can use the unit procs, without the UI i18n
................................................................................
  Windows, SysUtils, Classes,
  {$ifdef USEFORMCREATEHOOK}
  {$ifndef LVCL}
  Menus,
  {$endif}
  {$endif USEFORMCREATEHOOK}
  StdCtrls, Forms,
  SynCommons,     // some basic types
  SQLite3Commons; // need extended RTTI information


{$ifdef UNICODE}
{$undef ENHANCEDRTL} // no version of our Enhanced RTL exists for Delphi 2009 and up
{$endif}

{$ifdef LVCL}
// LVCL system.pas doesn't implement LoadResStringTranslate() and won't need it
{$define ENHANCEDRTL}
{$endif}


type
  {{ languages handled by this SQLite3i18n unit
   - include all languages known by WinXP SP2 without some unicode-only very
   rare languages; total count is 60
   - some languages (Japanase, Chinese, Arabic) may need specific language
   pack installed on western/latin version of windows
   - lngEnglish is the default language of the executable, used as reference
   for all other translation, and included into executable (no EN.msg file
   will never be loaded) }
................................................................................
  const CustomCaptions: array of WinAnsiString);
{$endif}

{$ifndef ENHANCEDRTL}
/// our hooked procedure for reading a string resource
// - the default one in System.pas unit is replaced by this one
// - this function add caching and on the fly translation (if LoadResStringTranslate
// is defined in SQLite3Commons unit)
// - use "string" type, i.e. UnicodeString for Delphi 2009 and up
function LoadResString(ResStringRec: PResStringRec): string;
{$endif}


/// convert any generic VCL Text into an UTF-8 encoded String
// - same as SynCommons.StringToUTF8()
................................................................................

/// convert an UTF-8 encoded text into a VCL-ready string
// - same as SynCommons.UTF8ToString()
function U2S(const Text: RawUTF8): string;
  {$ifdef HASINLINE}inline;{$endif}

/// convert a custom date/time into a VCL-ready string
// - this function must be assigned to i18nDateText global var of SQLite3Commons unit
// - wrapper to Language.DateTimeToText method
function Iso2S(Iso: TTimeLog): string;


implementation

uses
................................................................................
  GetLocaleFormatSettings(LCID_US,SettingsUS);
  {$endif}
  // avoid call nil functions -> set default function to point to
  i18nCompareStr := {$ifdef ENHANCEDRTL}CompareStr{$else}i18nInnerCompareStr{$endif};
  move(NormToUpper,i18nToUpper,sizeof(NormToUpper));
  i18nCompareText := i18nInnerCompareText;
{$ifndef ENHANCEDRTL}
  RedirectCode(@System.LoadResString,@SQLite3i18n.LoadResString,@BackupLoadResString);
  InitializeCriticalSection(CacheResCriticalSection);
{$endif}
{$ifndef NOI18N}
  LangInit; // do redirection + init user default locale (from Win32 or registry)
  i18nDateText :=  Iso2S; // for SQLite3Commons unit
{$endif}

finalization
{$ifndef NOI18N}
  FreeAndNil(Language);
{$ifdef USEFORMCREATEHOOK}
  if OriginalForm[0]<>0 then begin
|


|







 







|







 







|
|







 







>







 







|







 







|
|













|







 







|







 







|







 







|




|







1
2
3
4
5
6
7
8
9
10
11
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
...
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
...
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
....
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
/// internationalization (i18n) routines and classes for mORMot
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit mORMoti18n;

(*
    This file is part of Synopse mORMot framework.

    Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****


    i18n routines for the Synopse mORMot framework
   ************************************************

   - works internaly with the string type, that is AnsiString with code pages
     and charsets for compiler versions earlier than Delphi 2009, and
     UnicodeString since Delphi 2009 and up -> so it's 100% VCL compatible
   - can load language definition files encoded in Unicode or UTF8
   - auto-call SetThreadLocale() for full application i18n
   - update default locale settings values (date,currency..)
................................................................................
    - now Iso2S() - i.e. overriden i18nDateText global function pointer - will
      handle a date-only or time-only supplied value as expected

    Version 1.17
    - some refactoring about process in-memory code patching

    Version 1.18
	- renamed SQLite3i18n.pas to mORMoti18n.pas 
    - introducing TSQLPropInfo* classes to decouple ORM definitions from RTTI


*)

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

................................................................................
// must be set globally for the whole application

{.$define ENHANCEDRTL}
{ define this if you DID install our Enhanced Runtime library: it has already
  hacked the "legacy" LoadResString() procedure and added a LoadResStringTranslate()
  - it will be unset automaticaly (see below) for Delphi 2009 and up, since
    no version of our Enhanced RTL exists for these compilers 
  - this conditional must be defined in both mORMot.pas and mORmoti18n.pas units,
    or (even better) globally in the Project options }

{.$define USESHARP}
// if defined, $$,$$$,$$$ are replaced with some globals in _()

{$ifndef NOI18N}
  // with this global define, you can use the unit procs, without the UI i18n
................................................................................
  Windows, SysUtils, Classes,
  {$ifdef USEFORMCREATEHOOK}
  {$ifndef LVCL}
  Menus,
  {$endif}
  {$endif USEFORMCREATEHOOK}
  StdCtrls, Forms,
  SynCommons,     // some basic types and functions
  mORmot;         // need extended RTTI information


{$ifdef UNICODE}
{$undef ENHANCEDRTL} // no version of our Enhanced RTL exists for Delphi 2009 and up
{$endif}

{$ifdef LVCL}
// LVCL system.pas doesn't implement LoadResStringTranslate() and won't need it
{$define ENHANCEDRTL}
{$endif}


type
  {{ languages handled by this mORMoti18n unit
   - include all languages known by WinXP SP2 without some unicode-only very
   rare languages; total count is 60
   - some languages (Japanase, Chinese, Arabic) may need specific language
   pack installed on western/latin version of windows
   - lngEnglish is the default language of the executable, used as reference
   for all other translation, and included into executable (no EN.msg file
   will never be loaded) }
................................................................................
  const CustomCaptions: array of WinAnsiString);
{$endif}

{$ifndef ENHANCEDRTL}
/// our hooked procedure for reading a string resource
// - the default one in System.pas unit is replaced by this one
// - this function add caching and on the fly translation (if LoadResStringTranslate
// is defined in mORMot.pas unit)
// - use "string" type, i.e. UnicodeString for Delphi 2009 and up
function LoadResString(ResStringRec: PResStringRec): string;
{$endif}


/// convert any generic VCL Text into an UTF-8 encoded String
// - same as SynCommons.StringToUTF8()
................................................................................

/// convert an UTF-8 encoded text into a VCL-ready string
// - same as SynCommons.UTF8ToString()
function U2S(const Text: RawUTF8): string;
  {$ifdef HASINLINE}inline;{$endif}

/// convert a custom date/time into a VCL-ready string
// - this function must be assigned to i18nDateText global var of mORMot.pas unit
// - wrapper to Language.DateTimeToText method
function Iso2S(Iso: TTimeLog): string;


implementation

uses
................................................................................
  GetLocaleFormatSettings(LCID_US,SettingsUS);
  {$endif}
  // avoid call nil functions -> set default function to point to
  i18nCompareStr := {$ifdef ENHANCEDRTL}CompareStr{$else}i18nInnerCompareStr{$endif};
  move(NormToUpper,i18nToUpper,sizeof(NormToUpper));
  i18nCompareText := i18nInnerCompareText;
{$ifndef ENHANCEDRTL}
  RedirectCode(@System.LoadResString,@mORmoti18n.LoadResString,@BackupLoadResString);
  InitializeCriticalSection(CacheResCriticalSection);
{$endif}
{$ifndef NOI18N}
  LangInit; // do redirection + init user default locale (from Win32 or registry)
  i18nDateText :=  Iso2S; // for mORMot.pas unit
{$endif}

finalization
{$ifndef NOI18N}
  FreeAndNil(Language);
{$ifdef USEFORMCREATEHOOK}
  if OriginalForm[0]<>0 then begin

Changes to SynCommons.pas.

596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
....
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
....
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
....
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
....
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
....
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
....
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
....
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
....
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
....
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
....
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
....
7145
7146
7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
  PRawUnicode = ^RawUnicode;
  PRawUTF8 = ^RawUTF8;
  PWinAnsiString = ^WinAnsiString;
  PWinAnsiChar = type PAnsiChar;

  /// a simple wrapper to UTF-8 encoded zero-terminated PAnsiChar
  // - PAnsiChar is used only for Win-Ansi encoded text
  // - the Synopse SQLite3 framework uses mostly this PUTF8Char type,
  // because all data is internaly stored and expected to be UTF-8 encoded
  PUTF8Char = type PAnsiChar;
  PPUTF8Char = ^PUTF8Char;

  /// a Row/Col array of PUTF8Char, for containing sqlite3_get_table() result
  TPUtf8CharArray = array[0..MaxInt div SizeOf(PUTF8Char)-1] of PUTF8Char;
  PPUtf8CharArray = ^TPUtf8CharArray;
................................................................................

/// convert a Win-Ansi encoded buffer into a Delphi 2009+ Unicode string
// - this function is faster than default RTL, since use no Win32 API call
function WinAnsiToUnicodeString(WinAnsi: PAnsiChar; WinAnsiLen: integer): UnicodeString;
{$endif}

/// convert any generic VCL Text into an UTF-8 encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in SQLite3i18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringToUTF8(const Text: string): RawUTF8;
  {$ifdef HASINLINE}inline;{$endif}
................................................................................
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringBufferToUtf8(Dest: PUTF8Char; Source: PChar; SourceChars: PtrInt): PUTF8Char;

/// convert any generic VCL Text into a Raw Unicode encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in SQLite3i18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringToRawUnicode(const S: string): RawUnicode; overload;

/// convert any generic VCL Text into a SynUnicode encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in SQLite3i18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringToSynUnicode(const S: string): SynUnicode; 

/// convert any generic VCL Text into a Raw Unicode encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in SQLite3i18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringToRawUnicode(P: PChar; L: integer): RawUnicode; overload;

................................................................................
procedure RawUnicodeToString(P: PWideChar; L: integer; var result: string); overload;

/// convert any SynUnicode encoded string into a generic VCL Text
function SynUnicodeToString(const U: SynUnicode): string; 
  {$ifdef HASINLINE}inline;{$endif}

/// convert any UTF-8 encoded String into a generic VCL Text
// - it's prefered to use TLanguageFile.UTF8ToString() in SQLite3i18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function UTF8ToString(const Text: RawUTF8): string; 
  {$ifdef HASINLINE}inline;{$endif}

/// convert any UTF-8 encoded buffer into a generic VCL Text
// - it's prefered to use TLanguageFile.UTF8ToString() in SQLite3i18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function UTF8DecodeToString(P: PUTF8Char; L: integer): string; overload;
  {$ifdef UNICODE}inline;{$endif}
................................................................................
// - note that cardinal values should be type-casted to Int64() (otherwise
// the integer mapped value will be transmitted, therefore wrongly)
function FormatUTF8(Format: PUTF8Char; const Args: array of const): RawUTF8; overload;

/// fast Format() function replacement, handling % and ? parameters
// - will include Args[] for every % in Format
// - will inline Params[] for every ? in Format, handling special "inlined"
// parameters, as exected by SQLite3Commons, i.e. :(1234): for numerical
// values, and :('quoted '' string'): for textual values
// - resulting string has no length limit and uses fast concatenation
// - maximum count of supplied argument in Args is 12
// - note that cardinal values should be type-casted to Int64() (otherwise
// the integer mapped value will be transmitted, therefore wrongly)
function FormatUTF8(Format: PUTF8Char; const Args, Params: array of const): RawUTF8; overload;

................................................................................
procedure VarRecToUTF8(const V: TVarRec; var result: RawUTF8);

/// fast concatenation of several AnsiStrings
function RawByteStringArrayConcat(const Values: array of RawByteString): RawByteString;

type
  /// function prototype used internally for UTF-8 buffer comparaison
  // - used in SQLite3commons during TSQLTable rows sort and by TSQLQuery
  TUTF8Compare = function(P1,P2: PUTF8Char): PtrInt;

{$ifndef ISDELPHI2007ANDUP}
type
  TBytes = array of byte;
{$endif}

................................................................................
    property BlobData: RawByteString read GetBlobData write SetBlobData;
  end;



{$ifndef PUREPASCAl}
/// helper to retrieve the text of an enumerate item
// - you'd better use RTTI related classes of SQLite3Commons unit
function GetEnumName(aTypeInfo: pointer; aIndex: integer): PShortString;
{$endif}


/// check equality of two records by content
// - will handle packed records, with binaries (byte, word, integer...) and
// string types properties
................................................................................
    fSubPropAccess: TObjectListPropertyHashedAccessProp;
    function IntHash(const Elem): cardinal;
    function IntComp(const A,B): integer;
    procedure IntHashValid;
  public
    /// initialize the class instance with the corresponding callback in order
    // to handle sub-property hashing and search
    // - see TSetWeakZeroClass in SQLite3Commons unit as example:
    // !  function WeakZeroClassSubProp(aObject: TObject): TObject;
    // !  begin
    // !    result := TSetWeakZeroInstance(aObject).fInstance;
    // !  end;
    // - by default, aHashElement/aCompare will hash/search for pointers:
    // you can specify the hash/search methods according to your sub property
    // (e.g. HashAnsiStringI/SortDynArrayAnsiStringI for a RawUTF8)
................................................................................
  public
    /// perform the validation action to the specified value
    // - the value is expected by be UTF-8 text, as generated by
    // TPropInfo.GetValue e.g.
    // - if the validation failed, must return FALSE and put some message in
    // ErrorMsg (translated into the current language: you could e.g. use
    // a resourcestring and a SysUtils.Format() call for automatic translation
    // via the SQLite3i18n unit - you can leave ErrorMsg='' to trigger a
    // generic error message from clas name ('"Validate email" rule failed'
    // for TSynValidateEmail class e.g.)
    // - if the validation passed, will return TRUE
    function Process(FieldIndex: integer; const Value: RawUTF8; var ErrorMsg: string): boolean;
      virtual; abstract;
  end;

................................................................................
// - not the best, but simple and efficient code - perfect for THasher
function kr32(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal;

var
  /// the default hasher used by TDynArrayHashed()
  // - is set to kr32() function above
  // - should be set to faster and more accurate crc32() function if available
  // (this is what SQLite3Commons unit does in its initialization block) 
  DefaultHasher: THasher;

/// retrieve a particular bit status from a bit array
function GetBit(const Bits; aIndex: PtrInt): boolean;
  {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} {$endif}

/// set a particular bit into a bit array
................................................................................


{$ifndef ENHANCEDRTL}
{$ifndef LVCL} { don't define these twice }

var
  /// these procedure type must be defined if a default system.pas is used
  // - SQLite3i18n unit will hack default LoadResString() procedure
  // - already defined in our Extended system.pas unit
  // - needed with FPC, Delphi 2009 and up, i.e. when ENHANCEDRTL is not defined
  // - expect generic "string" type, i.e. UnicodeString for Delphi 2009+
  // - not needed with the LVCL framework (we should be on server side)
  LoadResStringTranslate: procedure(var Text: string) = nil;

  /// current LoadResString() cached entries count
  // - i.e. resourcestring caching for faster use
  // - used only if a default system.pas is used, not our Extended version
  // - defined here, but resourcestring caching itself is implemented in the
  // SQLite3i18n.pas unit, if the ENHANCEDRTL conditional is not defined
  CacheResCount: integer = -1;

{$endif}
{$endif}


/// log a message to a local text file
................................................................................
    // - this default implementation will just write the class name and its hexa
    // pointer value, and handle TList, TCollections and TStrings - for instance:
    // ! TSynLog.Add.Log(sllDebug,GarbageCollector);
    // will append this line to the log:
    // $ 20110330 10010005 debug {"TObjectList(00B1AD60)":["TObjectList(00B1AE20)","TObjectList(00B1AE80)"]}
    // - if aInstance is an Exception, it will handle its class name and Message:
    // $ 20110330 10010005 debug "EClassName(00C2129A)":"Exception message" 
    // - use TSQLLog from SQLite3Commons unit to add the record content, written
    // as human readable JSON
    procedure Log(Level: TSynLogInfo; aInstance: TObject); overload;
    /// call this method to add the content of most low-level types to the log
    // at a specified level
    // - this overriden implementation will write the value content,
    // written as human readable JSON: handle dynamic arrays and enumerations
    // - TSQLLog from SQLite3Commons unit will be able to write
    // TObject/TSQLRecord and sets content as JSON
    procedure Log(Level: TSynLogInfo; aName: PWinAnsiChar;
      aTypeInfo: pointer; var aValue; Instance: TObject=nil); overload;
    /// call this method to add the caller address to the log at the specified level
    // - if the debugging info is available from TSynMapFile, will log the
    // unit name, associated symbol and source code line
    procedure Log(Level: TSynLogInfo); overload;







|







 







|







 







|








|








|







 







|









|







 







|







 







|







 







|







 







|







 







|







 







|







 







|










|







 







|






|







596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
....
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
....
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
....
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
....
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
....
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
....
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
....
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
....
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
....
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
....
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
....
7145
7146
7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
  PRawUnicode = ^RawUnicode;
  PRawUTF8 = ^RawUTF8;
  PWinAnsiString = ^WinAnsiString;
  PWinAnsiChar = type PAnsiChar;

  /// a simple wrapper to UTF-8 encoded zero-terminated PAnsiChar
  // - PAnsiChar is used only for Win-Ansi encoded text
  // - the Synopse mORMot framework uses mostly this PUTF8Char type,
  // because all data is internaly stored and expected to be UTF-8 encoded
  PUTF8Char = type PAnsiChar;
  PPUTF8Char = ^PUTF8Char;

  /// a Row/Col array of PUTF8Char, for containing sqlite3_get_table() result
  TPUtf8CharArray = array[0..MaxInt div SizeOf(PUTF8Char)-1] of PUTF8Char;
  PPUtf8CharArray = ^TPUtf8CharArray;
................................................................................

/// convert a Win-Ansi encoded buffer into a Delphi 2009+ Unicode string
// - this function is faster than default RTL, since use no Win32 API call
function WinAnsiToUnicodeString(WinAnsi: PAnsiChar; WinAnsiLen: integer): UnicodeString;
{$endif}

/// convert any generic VCL Text into an UTF-8 encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in mORMoti18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringToUTF8(const Text: string): RawUTF8;
  {$ifdef HASINLINE}inline;{$endif}
................................................................................
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringBufferToUtf8(Dest: PUTF8Char; Source: PChar; SourceChars: PtrInt): PUTF8Char;

/// convert any generic VCL Text into a Raw Unicode encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in mORMoti18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringToRawUnicode(const S: string): RawUnicode; overload;

/// convert any generic VCL Text into a SynUnicode encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in mORMoti18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringToSynUnicode(const S: string): SynUnicode; 

/// convert any generic VCL Text into a Raw Unicode encoded String
// - it's prefered to use TLanguageFile.StringToUTF8() method in mORMoti18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function StringToRawUnicode(P: PChar; L: integer): RawUnicode; overload;

................................................................................
procedure RawUnicodeToString(P: PWideChar; L: integer; var result: string); overload;

/// convert any SynUnicode encoded string into a generic VCL Text
function SynUnicodeToString(const U: SynUnicode): string; 
  {$ifdef HASINLINE}inline;{$endif}

/// convert any UTF-8 encoded String into a generic VCL Text
// - it's prefered to use TLanguageFile.UTF8ToString() in mORMoti18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function UTF8ToString(const Text: RawUTF8): string; 
  {$ifdef HASINLINE}inline;{$endif}

/// convert any UTF-8 encoded buffer into a generic VCL Text
// - it's prefered to use TLanguageFile.UTF8ToString() in mORMoti18n,
// which will handle full i18n of your application
// - it will work as is with Delphi 2009+ (direct unicode conversion)
// - under older version of Delphi (no unicode), it will use the
// current RTL codepage, as with WideString conversion (but without slow
// WideString usage)
function UTF8DecodeToString(P: PUTF8Char; L: integer): string; overload;
  {$ifdef UNICODE}inline;{$endif}
................................................................................
// - note that cardinal values should be type-casted to Int64() (otherwise
// the integer mapped value will be transmitted, therefore wrongly)
function FormatUTF8(Format: PUTF8Char; const Args: array of const): RawUTF8; overload;

/// fast Format() function replacement, handling % and ? parameters
// - will include Args[] for every % in Format
// - will inline Params[] for every ? in Format, handling special "inlined"
// parameters, as exected by mORMot.pas unit, i.e. :(1234): for numerical
// values, and :('quoted '' string'): for textual values
// - resulting string has no length limit and uses fast concatenation
// - maximum count of supplied argument in Args is 12
// - note that cardinal values should be type-casted to Int64() (otherwise
// the integer mapped value will be transmitted, therefore wrongly)
function FormatUTF8(Format: PUTF8Char; const Args, Params: array of const): RawUTF8; overload;

................................................................................
procedure VarRecToUTF8(const V: TVarRec; var result: RawUTF8);

/// fast concatenation of several AnsiStrings
function RawByteStringArrayConcat(const Values: array of RawByteString): RawByteString;

type
  /// function prototype used internally for UTF-8 buffer comparaison
  // - used in mORMot.pas unit during TSQLTable rows sort and by TSQLQuery
  TUTF8Compare = function(P1,P2: PUTF8Char): PtrInt;

{$ifndef ISDELPHI2007ANDUP}
type
  TBytes = array of byte;
{$endif}

................................................................................
    property BlobData: RawByteString read GetBlobData write SetBlobData;
  end;



{$ifndef PUREPASCAl}
/// helper to retrieve the text of an enumerate item
// - you'd better use RTTI related classes of mORMot.pas unit
function GetEnumName(aTypeInfo: pointer; aIndex: integer): PShortString;
{$endif}


/// check equality of two records by content
// - will handle packed records, with binaries (byte, word, integer...) and
// string types properties
................................................................................
    fSubPropAccess: TObjectListPropertyHashedAccessProp;
    function IntHash(const Elem): cardinal;
    function IntComp(const A,B): integer;
    procedure IntHashValid;
  public
    /// initialize the class instance with the corresponding callback in order
    // to handle sub-property hashing and search
    // - see TSetWeakZeroClass in mORMot.pas unit as example:
    // !  function WeakZeroClassSubProp(aObject: TObject): TObject;
    // !  begin
    // !    result := TSetWeakZeroInstance(aObject).fInstance;
    // !  end;
    // - by default, aHashElement/aCompare will hash/search for pointers:
    // you can specify the hash/search methods according to your sub property
    // (e.g. HashAnsiStringI/SortDynArrayAnsiStringI for a RawUTF8)
................................................................................
  public
    /// perform the validation action to the specified value
    // - the value is expected by be UTF-8 text, as generated by
    // TPropInfo.GetValue e.g.
    // - if the validation failed, must return FALSE and put some message in
    // ErrorMsg (translated into the current language: you could e.g. use
    // a resourcestring and a SysUtils.Format() call for automatic translation
    // via the mORMoti18n unit - you can leave ErrorMsg='' to trigger a
    // generic error message from clas name ('"Validate email" rule failed'
    // for TSynValidateEmail class e.g.)
    // - if the validation passed, will return TRUE
    function Process(FieldIndex: integer; const Value: RawUTF8; var ErrorMsg: string): boolean;
      virtual; abstract;
  end;

................................................................................
// - not the best, but simple and efficient code - perfect for THasher
function kr32(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal;

var
  /// the default hasher used by TDynArrayHashed()
  // - is set to kr32() function above
  // - should be set to faster and more accurate crc32() function if available
  // (this is what mORMot.pas unit does in its initialization block) 
  DefaultHasher: THasher;

/// retrieve a particular bit status from a bit array
function GetBit(const Bits; aIndex: PtrInt): boolean;
  {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} {$endif}

/// set a particular bit into a bit array
................................................................................


{$ifndef ENHANCEDRTL}
{$ifndef LVCL} { don't define these twice }

var
  /// these procedure type must be defined if a default system.pas is used
  // - mORMoti18n.pas unit will hack default LoadResString() procedure
  // - already defined in our Extended system.pas unit
  // - needed with FPC, Delphi 2009 and up, i.e. when ENHANCEDRTL is not defined
  // - expect generic "string" type, i.e. UnicodeString for Delphi 2009+
  // - not needed with the LVCL framework (we should be on server side)
  LoadResStringTranslate: procedure(var Text: string) = nil;

  /// current LoadResString() cached entries count
  // - i.e. resourcestring caching for faster use
  // - used only if a default system.pas is used, not our Extended version
  // - defined here, but resourcestring caching itself is implemented in the
  // mORMoti18n.pas unit, if the ENHANCEDRTL conditional is not defined
  CacheResCount: integer = -1;

{$endif}
{$endif}


/// log a message to a local text file
................................................................................
    // - this default implementation will just write the class name and its hexa
    // pointer value, and handle TList, TCollections and TStrings - for instance:
    // ! TSynLog.Add.Log(sllDebug,GarbageCollector);
    // will append this line to the log:
    // $ 20110330 10010005 debug {"TObjectList(00B1AD60)":["TObjectList(00B1AE20)","TObjectList(00B1AE80)"]}
    // - if aInstance is an Exception, it will handle its class name and Message:
    // $ 20110330 10010005 debug "EClassName(00C2129A)":"Exception message" 
    // - use TSQLLog from mORMot.pas unit to add the record content, written
    // as human readable JSON
    procedure Log(Level: TSynLogInfo; aInstance: TObject); overload;
    /// call this method to add the content of most low-level types to the log
    // at a specified level
    // - this overriden implementation will write the value content,
    // written as human readable JSON: handle dynamic arrays and enumerations
    // - TSQLLog from mORMot.pas unit will be able to write
    // TObject/TSQLRecord and sets content as JSON
    procedure Log(Level: TSynLogInfo; aName: PWinAnsiChar;
      aTypeInfo: pointer; var aValue; Instance: TObject=nil); overload;
    /// call this method to add the caller address to the log at the specified level
    // - if the debugging info is available from TSynMapFile, will log the
    // unit name, associated symbol and source code line
    procedure Log(Level: TSynLogInfo); overload;

Changes to SynCrtSock.pas.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    // order to start the server
    constructor Create(CreateSuspended: Boolean);
    /// release all associated memory and handles
    destructor Destroy; override;
    /// will clone this thread into multiple other threads
    // - could speed up the process on multi-core CPU
    // - will work only if the OnProcess property was set (this is the case
    // e.g. in TSQLite3HttpServer.Create() constructor)
    // - maximum value is 256 - higher should not be worth it
    procedure Clone(ChildThreadCount: integer);
    /// register the URLs to Listen On
    // - e.g. AddUrl('root','888')
    // - aDomainName could be either a fully qualified case-insensitive domain
    // name, an IPv4 or IPv6 literal string, or a wildcard ('+' will bound
    // to all domain names for the specified port, '*' will accept the request







|







 







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
    // order to start the server
    constructor Create(CreateSuspended: Boolean);
    /// release all associated memory and handles
    destructor Destroy; override;
    /// will clone this thread into multiple other threads
    // - could speed up the process on multi-core CPU
    // - will work only if the OnProcess property was set (this is the case
    // e.g. in TSQLHttpServer.Create() constructor)
    // - maximum value is 256 - higher should not be worth it
    procedure Clone(ChildThreadCount: integer);
    /// register the URLs to Listen On
    // - e.g. AddUrl('root','888')
    // - aDomainName could be either a fully qualified case-insensitive domain
    // name, an IPv4 or IPv6 literal string, or a wildcard ('+' will bound
    // to all domain names for the specified port, '*' will accept the request

Changes to SynCrypto.pas.

1
2
3
4
5
6
7
8
9
10
11
12
..
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// fast cryptographic routines (hashing and cypher)
// - implements AES, XOR, ADLER32, MD5, SHA1, SHA256 algorithms
// - optimized for speed (tuned assembler and VIA PADLOCK optional support)
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SynCrypto;

(*
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):




|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
..
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// fast cryptographic routines (hashing and cypher)
// - implements AES, XOR, ADLER32, MD5, SHA1, SHA256 algorithms
// - optimized for speed (tuned assembler and VIA PADLOCK optional support)
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynCrypto;

(*
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse mORMot framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):

Changes to SynDB.pas.

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
....
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
....
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
....
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
....
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
....
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891

type
  /// generic Exception type, as used by the SynDB unit
  ESQLDBException = class(Exception);

  /// the handled field/parameter/column types by this unit
  // - this will map low-level database-level access types, not high-level
  // Delphi types as TSQLFieldType defined in SQLite3Commons
  // - for instance, it can be mapped to standard SQLite3 generic types, i.e.
  // NULL, INTEGER, REAL, TEXT, BLOB (with the addition of a ftCurrency and
  // ftDate type, for better support of most DB engines)
  // see @http://www.sqlite.org/datatype3.html
  // - the only string type handled here uses UTF-8 encoding (implemented
  // using our RawUTF8 type), for cross-Delphi true Unicode process
  TSQLDBFieldType =
................................................................................
    // & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
    // - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
    // & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
    // - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
    // format and contains true BLOB data
    // - if ReturnedRowCount points to an integer variable, it will be filled with
    // the number of row data returned (excluding field names)
    // - similar to corresponding TSQLRequest.Execute method in SQLite3 unit
    function FetchAllAsJSON(Expanded: boolean; ReturnedRowCount: PPtrInt=nil): RawUTF8;
{$ifndef DELPHI5OROLDER}
    /// create a TSQLDBRowVariantType able to access any field content via late binding
    // - i.e. you can use Data.Name to access the 'Name' column of the current row
    // - this Variant will point to the corresponding TSQLDBStatement instance,
    // so it's not necessary to retrieve its value for each row
    // - typical use is:
................................................................................
     - return TRUE on success, with data ready to be retrieved by Column*()
     - return FALSE if no more row is available (e.g. if the SQL statement
      is not a SELECT but an UPDATE or INSERT command)
     - access the first or next row of data from the SQL Statement result:
       if SeekFirst is TRUE, will put the cursor on the first row of results,
       otherwise, it will fetch one row of data, to be called within a loop
     - should raise an Exception on any error
     - typical use may be (see also e.g. the SQLite3DB unit):
     ! var Query: TSQLDBStatement;
     ! begin
     !   Query := Props.NewThreadSafeStatementPrepared('select AccountNumber from Sales.Customer where AccountNumber like ?', ['AW000001%'],true);
     !   if Query<>nil then
     !   try
     !     assert(SameTextU(Query.ColumnName(0),'AccountNumber'));
     !     while Query.Step do // loop through all matching data rows
................................................................................
    // - if Expanded is true, JSON data is an array of objects, for direct use
    // with any Ajax or .NET client:
    // & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
    // - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
    // & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
    // - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
    // format and contains true BLOB data
    // - similar to corresponding TSQLRequest.Execute method in SQLite3 unit
    // - returns the number of row data returned (excluding field names)
    // - warning: TSQLRestServerStaticExternal.EngineRetrieve in SQLite3DB
    // expects the Expanded=true format to return '[{...}]'#10
    function FetchAllToJSON(JSON: TStream; Expanded: boolean): PtrInt;
    // Append all rows content as a CSV stream
    // - CSV data is added to the supplied TStream, with UTF-8 encoding
    // - if Tab=TRUE, will use TAB instead of ',' between columns
    // - you can customize the ',' separator - use e.g. the global ListSeparator
    // variable (from SysUtils) to reflect the current system definition (some
................................................................................
    // & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
    // - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
    // & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
    // - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
    // format and contains true BLOB data
    // - if ReturnedRowCount points to an integer variable, it will be filled with
    // the number of row data returned (excluding field names)
    // - similar to corresponding TSQLRequest.Execute method in SQLite3 unit
    function FetchAllAsJSON(Expanded: boolean; ReturnedRowCount: PPtrInt=nil): RawUTF8;

    /// the associated database connection
    property Connection: TSQLDBConnection read fConnection;
    /// the prepared SQL statement, as supplied to Prepare() method
    property SQL: RawUTF8 read fSQL;
    /// the prepared SQL statement, with all '?' changed into the supplied
................................................................................
    // CoInitialize was made, for instance via a method defined as such:
    // ! procedure TMyServer.OnHttpThreadTerminate(Sender: TObject);
    // ! begin
    // !   fMyConnectionProps.EndCurrentThread;
    // ! end;
    // - this method shall be called from the thread about to be terminated: e.g.
    // if you call it from the main thread, it may fail to release resources
    // - within the mORMot server, SQLite3DB unit will call this method
    // for every terminating thread created for TSQLRestServerNamedPipeResponse
    // or TSQLite3HttpServer multi-thread process  
    procedure EndCurrentThread; virtual;
  end;

  /// a structure used to store a standard binding parameter
  // - you can use your own internal representation of parameters
  // (TOleDBStatement use its own TOleDBStatementParam type), but
  // this type can be used to implement a generic parameter
................................................................................
    property Params[aIndex: integer]: TParam read GetParam;
  end;

{$endif EMULATES_TQUERY}

var
  /// the TSynLog class used for logging for all our SynDB related units
  // - you may override it with TSQLLog, if available from SQLite3Commons
  // - since not all exceptions are handled specificaly by this unit, you
  // may better use a common TSynLog class for the whole application or module
  SynDBLog: TSynLogClass=TSynLog;



implementation







|







 







|







 







|







 







|

|







 







|







 







|

|







 







|







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
....
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
....
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
....
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
....
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
....
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891

type
  /// generic Exception type, as used by the SynDB unit
  ESQLDBException = class(Exception);

  /// the handled field/parameter/column types by this unit
  // - this will map low-level database-level access types, not high-level
  // Delphi types as TSQLFieldType defined in mORMot.pas
  // - for instance, it can be mapped to standard SQLite3 generic types, i.e.
  // NULL, INTEGER, REAL, TEXT, BLOB (with the addition of a ftCurrency and
  // ftDate type, for better support of most DB engines)
  // see @http://www.sqlite.org/datatype3.html
  // - the only string type handled here uses UTF-8 encoding (implemented
  // using our RawUTF8 type), for cross-Delphi true Unicode process
  TSQLDBFieldType =
................................................................................
    // & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
    // - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
    // & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
    // - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
    // format and contains true BLOB data
    // - if ReturnedRowCount points to an integer variable, it will be filled with
    // the number of row data returned (excluding field names)
    // - similar to corresponding TSQLRequest.Execute method in SynSQLite3 unit
    function FetchAllAsJSON(Expanded: boolean; ReturnedRowCount: PPtrInt=nil): RawUTF8;
{$ifndef DELPHI5OROLDER}
    /// create a TSQLDBRowVariantType able to access any field content via late binding
    // - i.e. you can use Data.Name to access the 'Name' column of the current row
    // - this Variant will point to the corresponding TSQLDBStatement instance,
    // so it's not necessary to retrieve its value for each row
    // - typical use is:
................................................................................
     - return TRUE on success, with data ready to be retrieved by Column*()
     - return FALSE if no more row is available (e.g. if the SQL statement
      is not a SELECT but an UPDATE or INSERT command)
     - access the first or next row of data from the SQL Statement result:
       if SeekFirst is TRUE, will put the cursor on the first row of results,
       otherwise, it will fetch one row of data, to be called within a loop
     - should raise an Exception on any error
     - typical use may be (see also e.g. the mORMotDB unit):
     ! var Query: TSQLDBStatement;
     ! begin
     !   Query := Props.NewThreadSafeStatementPrepared('select AccountNumber from Sales.Customer where AccountNumber like ?', ['AW000001%'],true);
     !   if Query<>nil then
     !   try
     !     assert(SameTextU(Query.ColumnName(0),'AccountNumber'));
     !     while Query.Step do // loop through all matching data rows
................................................................................
    // - if Expanded is true, JSON data is an array of objects, for direct use
    // with any Ajax or .NET client:
    // & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
    // - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
    // & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
    // - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
    // format and contains true BLOB data
    // - similar to corresponding TSQLRequest.Execute method in SynSQLite3 unit
    // - returns the number of row data returned (excluding field names)
    // - warning: TSQLRestServerStaticExternal.EngineRetrieve in mORMotDB unit
    // expects the Expanded=true format to return '[{...}]'#10
    function FetchAllToJSON(JSON: TStream; Expanded: boolean): PtrInt;
    // Append all rows content as a CSV stream
    // - CSV data is added to the supplied TStream, with UTF-8 encoding
    // - if Tab=TRUE, will use TAB instead of ',' between columns
    // - you can customize the ',' separator - use e.g. the global ListSeparator
    // variable (from SysUtils) to reflect the current system definition (some
................................................................................
    // & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
    // - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
    // & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
    // - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
    // format and contains true BLOB data
    // - if ReturnedRowCount points to an integer variable, it will be filled with
    // the number of row data returned (excluding field names)
    // - similar to corresponding TSQLRequest.Execute method in SynSQLite3 unit
    function FetchAllAsJSON(Expanded: boolean; ReturnedRowCount: PPtrInt=nil): RawUTF8;

    /// the associated database connection
    property Connection: TSQLDBConnection read fConnection;
    /// the prepared SQL statement, as supplied to Prepare() method
    property SQL: RawUTF8 read fSQL;
    /// the prepared SQL statement, with all '?' changed into the supplied
................................................................................
    // CoInitialize was made, for instance via a method defined as such:
    // ! procedure TMyServer.OnHttpThreadTerminate(Sender: TObject);
    // ! begin
    // !   fMyConnectionProps.EndCurrentThread;
    // ! end;
    // - this method shall be called from the thread about to be terminated: e.g.
    // if you call it from the main thread, it may fail to release resources
    // - within the mORMot server, mORMotDB unit will call this method
    // for every terminating thread created for TSQLRestServerNamedPipeResponse
    // or TSQLHttpServer multi-thread process  
    procedure EndCurrentThread; virtual;
  end;

  /// a structure used to store a standard binding parameter
  // - you can use your own internal representation of parameters
  // (TOleDBStatement use its own TOleDBStatementParam type), but
  // this type can be used to implement a generic parameter
................................................................................
    property Params[aIndex: integer]: TParam read GetParam;
  end;

{$endif EMULATES_TQUERY}

var
  /// the TSynLog class used for logging for all our SynDB related units
  // - you may override it with TSQLLog, if available from mORMot.pas
  // - since not all exceptions are handled specificaly by this unit, you
  // may better use a common TSynLog class for the whole application or module
  SynDBLog: TSynLogClass=TSynLog;



implementation

Changes to SynDBOracle.pas.

357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
     - so this Value should not be used typecasted to a Variant
     - the specified Temp variable will be used for temporary storage of
       varString/varAny values
     - this implementation will retrieve the data with no temporary variable,
       and handling ftCurrency/NUMBER(22,0) as fast as possible, directly from
       the memory buffers returned by OCI: it will ensure best performance
       possible when called from TSQLVirtualTableCursorExternal.Column method
       as defined in SQlite3DB (i.e. mORMot external DB access) }
    function ColumnToVarData(Col: Integer; var Value: TVarData;
      var Temp: RawByteString): TSQLDBFieldType; override;
    {{ append all columns values of the current Row to a JSON stream
     - will use WR.Expand to guess the expected output format
     - fast overriden implementation with no temporary variable (about 20%
       faster when run over high number of data rows) 
     - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"







|







357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
     - so this Value should not be used typecasted to a Variant
     - the specified Temp variable will be used for temporary storage of
       varString/varAny values
     - this implementation will retrieve the data with no temporary variable,
       and handling ftCurrency/NUMBER(22,0) as fast as possible, directly from
       the memory buffers returned by OCI: it will ensure best performance
       possible when called from TSQLVirtualTableCursorExternal.Column method
       as defined in mORMotDB unit (i.e. mORMot external DB access) }
    function ColumnToVarData(Col: Integer; var Value: TVarData;
      var Temp: RawByteString): TSQLDBFieldType; override;
    {{ append all columns values of the current Row to a JSON stream
     - will use WR.Expand to guess the expected output format
     - fast overriden implementation with no temporary variable (about 20%
       faster when run over high number of data rows) 
     - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"

Changes to SynDBSQLite3.pas.

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  {$ifndef LVCL}
  Contnrs,
  {$endif}
  SynCommons,
  SynSQLite3,
  SynDB;

{ -------------- SQlite3 databese engine native connection  }

type
  /// will implement properties shared by the static SQLite3 engine
  TSQLDBSQLite3ConnectionProperties = class(TSQLDBConnectionProperties)
  private
    fUseMormotCollations: boolean;
    procedure SetUseMormotCollations(const Value: boolean);







|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  {$ifndef LVCL}
  Contnrs,
  {$endif}
  SynCommons,
  SynSQLite3,
  SynDB;

{ -------------- SQlite3 database engine native connection  }

type
  /// will implement properties shared by the static SQLite3 engine
  TSQLDBSQLite3ConnectionProperties = class(TSQLDBConnectionProperties)
  private
    fUseMormotCollations: boolean;
    procedure SetUseMormotCollations(const Value: boolean);

Changes to SynPdf.pas.

2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375

(*
    Windows Uniscribe APIs

    Uniscribe is a set of APIs that allow a high degree of control for fine
    typography and for processing complex scripts
    - see http://msdn.microsoft.com/en-us/library/dd374091(v=VS.85).aspx
    - used by both SynPDF and SQLite3Pages (for TRenderPages)
    - NO_USE_UNISCRIBE conditional can be set globaly for an application
      which doesn't need the UniScribe features
*)


{$ifdef USE_UNISCRIBE}
const







|







2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375

(*
    Windows Uniscribe APIs

    Uniscribe is a set of APIs that allow a high degree of control for fine
    typography and for processing complex scripts
    - see http://msdn.microsoft.com/en-us/library/dd374091(v=VS.85).aspx
    - used by both SynPDF.pas and mORMotReport.pas (for TGDIPages)
    - NO_USE_UNISCRIBE conditional can be set globaly for an application
      which doesn't need the UniScribe features
*)


{$ifdef USE_UNISCRIBE}
const

Changes to SynProject/ProjectEditorProgram.pas.

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
      with Project.Parse[i] do begin
        cbSADSection.Items.AddObject(SectionName,Project.Parse[i]);
        if SameText(SectionNameValue,V) then begin
          SAD.FillUnits(Project.Parse[i],false);
          cbSADSection.ItemIndex := i;
        end;
      end;
      V := ExtractFileName(P);        // V = 'SQlite3.pas'
      if cbSADSection.ItemIndex<0 then begin
        U := copy(V,1,length(V)-4);   // U = 'SQLite3'
        // no section defined -> may be 'D:\Dev\Lib\SynPdf.pas' -> search by unit
        for i := 0 to high(Project.Parse) do begin
          SAD.FillUnits(Project.Parse[i],false);
          if SAD.Units.FindName(U)<>nil then begin
            cbSADSection.ItemIndex := i;
            break;
          end;







|

|







185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
      with Project.Parse[i] do begin
        cbSADSection.Items.AddObject(SectionName,Project.Parse[i]);
        if SameText(SectionNameValue,V) then begin
          SAD.FillUnits(Project.Parse[i],false);
          cbSADSection.ItemIndex := i;
        end;
      end;
      V := ExtractFileName(P);        // V = 'mORMot.pas'
      if cbSADSection.ItemIndex<0 then begin
        U := copy(V,1,length(V)-4);   // U = 'mORMot'
        // no section defined -> may be 'D:\Dev\Lib\SynPdf.pas' -> search by unit
        for i := 0 to high(Project.Parse) do begin
          SAD.FillUnits(Project.Parse[i],false);
          if SAD.Units.FindName(U)<>nil then begin
            cbSADSection.ItemIndex := i;
            break;
          end;

Changes to SynProject/ProjectRTF.pas.

1
2
3
4
5
6
7
8
9
..
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/// text and RTF writing classes
// - this unit is part of SynProject, under GPL 3.0 license; version 1.15
unit ProjectRTF;

(*
    This file is part of SynProject.

    Synopse SynProject. Copyright (C) 2008-2011 Arnaud Bouchez
      Synopse Informatique - http://synopse.info
................................................................................
{.$define DIRECTPREVIEW}
{ if defined, documents can be previewed then exported as PDF directly }

{$define DIRECTEXPORTTOWORD}
{ if defined, documents are directly converted to .doc }

uses
  {$ifdef DIRECTPREVIEW}SQLite3Pages,{$endif}
  SysUtils, ProjectCommons, Classes;

{
   TStringWriter: fast buffered output

  var s: string;
      c: TStringWriter; // automatic Garbage Collector

|







 







|







1
2
3
4
5
6
7
8
9
..
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/// text and RTF writing classes
// - this unit is part of SynProject, under GPL 3.0 license; version 1.17
unit ProjectRTF;

(*
    This file is part of SynProject.

    Synopse SynProject. Copyright (C) 2008-2011 Arnaud Bouchez
      Synopse Informatique - http://synopse.info
................................................................................
{.$define DIRECTPREVIEW}
{ if defined, documents can be previewed then exported as PDF directly }

{$define DIRECTEXPORTTOWORD}
{ if defined, documents are directly converted to .doc }

uses
  {$ifdef DIRECTPREVIEW}mORMotReport,{$endif}
  SysUtils, ProjectCommons, Classes;

{
   TStringWriter: fast buffered output

  var s: string;
      c: TStringWriter; // automatic Garbage Collector

Changes to SynSQLite3.pas.

156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

{$define INCLUDE_FTS3}
{ define this if you want to include the FTS3/FTS4 feature into the library
  - FTS3 is an SQLite module implementing full-text search
  - will include also FTS4 extension module since 3.7.4
  - see http://www.sqlite.org/fts3.html for documentation
  - is defined by default, but can be unset to save about 50 KB of code size
  - should be defined for both SynSQLite3 and SQLite3 units }

{$ifdef INCLUDE_FTS3}
  {$define INCLUDE_TRACE} 
  { define this is you want to include the TRACE feature into the library
   - our C source code custom header will define SQLITE_OMIT_TRACE if FTS3/FST4
   is not defined }
{$endif}

{.$define USEFASTCALL}
{ use the fastcall calling convention to access the SQLite3 library
  - BCC32 -pr fastcall (=Delphi resgister) is buggy, don't know why
   (because of issues with BCC32 itself, or some obfuscated calls in source?)
  - should be defined for both SynSQLite3 and SQLite3 units }


{ ************ direct access to sqlite3.c / sqlite3.obj consts and functions }

type
  /// internaly store the SQLite3 database handle
  TSQLite3DB = type PtrUInt;







|












|







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

{$define INCLUDE_FTS3}
{ define this if you want to include the FTS3/FTS4 feature into the library
  - FTS3 is an SQLite module implementing full-text search
  - will include also FTS4 extension module since 3.7.4
  - see http://www.sqlite.org/fts3.html for documentation
  - is defined by default, but can be unset to save about 50 KB of code size
  - should be defined for both SynSQLite3 and mORMotSQLite3 units }

{$ifdef INCLUDE_FTS3}
  {$define INCLUDE_TRACE} 
  { define this is you want to include the TRACE feature into the library
   - our C source code custom header will define SQLITE_OMIT_TRACE if FTS3/FST4
   is not defined }
{$endif}

{.$define USEFASTCALL}
{ use the fastcall calling convention to access the SQLite3 library
  - BCC32 -pr fastcall (=Delphi resgister) is buggy, don't know why
   (because of issues with BCC32 itself, or some obfuscated calls in source?)
  - should be defined for both SynSQLite3 and mORMotSQLite3 units }


{ ************ direct access to sqlite3.c / sqlite3.obj consts and functions }

type
  /// internaly store the SQLite3 database handle
  TSQLite3DB = type PtrUInt;

Changes to SynSelfTests.pas.

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
...
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
...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
....
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
....
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
....
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
....
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
  SynDB,
{$endif}
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}
  SynSQLite3,
{$ifndef LVCL}
  SynDBSQLite3,
  SQLite3DB,
{$endif}
  SQLite3Commons,
  SQLite3,
  SQLite3HttpServer,
  SQLite3HttpClient,
{$endif}
{$endif}
{$endif}
  SynCommons;

{ ************ Unit-Testing classes and functions }

................................................................................
    /// low-level TSynValidate classes
    procedure _TSynValidate;
    /// low-level TSynLogFile class
    procedure _TSynLogFile;
  end;

  /// this test case will test most low-level functions, classes and types
  // defined and implemented in the SQLite3Commons unit
  TTestLowLevelTypes = class(TSynTestCase)
  published
{$ifndef DELPHI5OROLDER}
    /// some low-level RTTI access
    // - especially the field type retrieval from published properties
    procedure RTTI;
{$endif}
................................................................................
    procedure UrlEncoding;
    /// some low-level JSON encoding/decoding
    procedure EncodeDecodeJSON;
  end;

{$ifndef DELPHI5OROLDER}
  /// this test case will test some generic classes
  // defined and implemented in the SQLite3Commons unit
  TTestBasicClasses = class(TSynTestCase)
  published
    /// test the TSQLRecord class
    // - especially SQL auto generation, or JSON export/import
    procedure _TSQLRecord;
    /// test the digital signature of records
    procedure _TSQLRecordSigned;
    /// test the TSQLModel class
    procedure _TSQLModel;
  end;

  // a record mapping used in the test classes of the framework
  // - this class can be used for debugging purposes, with the database
  // created by TTestFileBased in SQLite3.pas
  // - this class will use 'People' as a table name
  TSQLRecordPeople = class(TSQLRecord)
  private
    fData: TSQLRawBlob;
    fFirstName: RawUTF8;
    fLastName: RawUTF8;
    fYearOfBirth: integer;
................................................................................
{$endif}

{$ifndef FPC}
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}
type
  /// this test case will test most functions, classes and types defined and
  // implemented in the SQLite3 unit, i.e. the SQLite3 engine itself,
  // used as a HTTP/1.1 server and client
  // - test a HTTP/1.1 server and client on the port 888 of the local machine
  // - require the 'test.db3' SQLite3 database file, as created by TTestFileBased
  TTestClientServerAccess = class(TSynTestCase)
  protected
    { these values are used internaly by the published methods below }
    Model: TSQLModel;
    DataBase: TSQLRestServerDB;
    Server: TSQLite3HttpServer;
    Client: TSQLRestClient;
    /// perform the tests of the current Client instance
    procedure ClientTest;
  public
    /// release used instances (e.g. http server) and memory
    destructor Destroy; override;
    /// this could be called as administrator for THttpApiServer to work
    class function RegisterAddUrl(OnlyDelete: boolean): string;
  published
    /// initialize a TSQLite3HttpServer instance
    // - uses the 'test.db3' SQLite3 database file generated by TTestSQLite3Engine
    // - creates and validates a HTTP/1.1 server on the port 888 of the local
    // machine, using the THttpApiServer (using kernel mode http.sys) class
    // if available
    procedure _TSQLite3HttpServer;
    /// validate the HTTP/1.1 client implementation
    // - by using a request of all records data
    procedure _TSQLite3HttpClient;
    /// validate the HTTP/1.1 client multi-query implementation with one
    // connection for the all queries
    // - this method keep alive the HTTP connection, so is somewhat faster
    // - it runs 1000 remote SQL queries, and check the JSON data retrieved
    // - the time elapsed for this step is computed, and displayed on the report
    procedure HttpClientKeepAlive;
    /// validate the HTTP/1.1 client multi-query implementation with one
................................................................................
    // communicating
    // - it then runs 1000 remote SQL queries, and check the JSON data retrieved
    // - the time elapsed for this step is computed, and displayed on the report
    procedure DirectInProcessAccess;
  end;

  /// a parent test case which will test most functions, classes and types defined
  // and implemented in the SQLite3 unit, i.e. the SQLite3 engine itself
  // - it should not be called directly, but through TTestFileBased,
  // TTestMemoryBased and TTestMemoryBased children
  TTestSQLite3Engine = class(TSynTestCase)
  protected
    { these values are used internaly by the published methods below }
    TempFileName: TFileName;
    IsMemory: boolean;
................................................................................
    // arrays published properties handling
    // - also test FTS implementation if INCLUDE_FTS3 conditional is defined
    // - test dynamic tables
    procedure _TSQLRestClientDB;
  end;

  /// this test case will test most functions, classes and types defined and
  // implemented in the SQLite3 unit, i.e. the SQLite3 engine itself,
  // with a file-based approach
  TTestFileBased = class(TTestSQLite3Engine);

  /// this test case will test most functions, classes and types defined and
  // implemented in the SQLite3 unit, i.e. the SQLite3 engine itself,
  // with a memory-based approach
  // - this class will also test the TSQLRestServerStatic class, and its
  // 100% Delphi simple database engine
  TTestMemoryBased = class(TTestSQLite3Engine);

  /// this test case will test most functions, classes and types defined and
  // implemented in the SQLite3 unit, i.e. the SQLite3 engine itself,
  // with a file-based approach
  // - purpose of this class is to test Write-Ahead Logging for the database
  TTestFileBasedWAL = class(TTestFileBased);

  /// this class defined two published methods of type TSQLRestServerCallBack in
  //  order to test the Server-Side ModelRoot/TableName/ID/MethodName RESTful model
  TSQLRestServerTest = class(TSQLRestServerDB)
................................................................................
        named A and B, as in the public TSQLRecordPeople.Sum() method,
        which implements the Client-Side of this service
      - Table nor ID are never used here }
    function Sum(var aParams: TSQLRestServerCallBackParams): Integer;
  end;

{$ifndef LVCL}
  /// a test case which will test most external DB functions of the SQLite3DB unit
  // - the external DB will be in fact a SynDBSQLite3 instance, expecting a
  // test.db3 SQlite3 file available in the current directory, populated with
  // some TSQLRecordPeople rows
  // - note that SQL statement caching at SQLite3 engine level makes those test
  // 2 times faster
  TTestExternalDatabase = class(TSynTestCase)
  protected
................................................................................
{$ifndef FPC}
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}

{ TTestClientServerAccess }

{$WARN SYMBOL_PLATFORM OFF}
procedure TTestClientServerAccess._TSQLite3HttpClient;
var Resp: TSQLTable;
begin
  Client := TSQLite3HttpClient.Create('127.0.0.1','888',Model);
  if (DebugHook<>0) and Client.InheritsFrom(TSQLite3HttpClientWinSock) then
    TSQLite3HttpClientWinSock(Client).Socket.TimeOut := 60000; // 1 minute time out
  Resp := Client.List([TSQLRecordPeople],'*');
  if CheckFailed(Resp<>nil) then
    exit;
  try
    Check(Resp.InheritsFrom(TSQLTableJSON));
    Check(Hash32(TSQLTableJSON(Resp).PrivateInternalCopy)=$352A6CCA);
  finally
................................................................................
{$WARN SYMBOL_PLATFORM ON}

class function TTestClientServerAccess.RegisterAddUrl(OnlyDelete: boolean): string;
begin
  result := THttpApiServer.AddUrlAuthorize('root','888',false,'+',OnlyDelete);
end;

procedure TTestClientServerAccess._TSQLite3HttpServer;
const
  MIMES: array[0..37] of TFileName = (
   'png','image/png',
   'PNg','image/png',
   'gif','image/gif',
   'tif','image/tiff',
   'tiff','image/tiff',
................................................................................
    Check(GetMimeContentType(nil,0,'toto.'+MIMES[i*2])=StringToAnsi7(MIMES[i*2+1]),MIMES[i*2]);
  Model := TSQLModel.Create([TSQLRecordPeople],'root');
  Check(Model<>nil);
  Check(Model.GetTableIndex('people')>=0);
  try
    DataBase := TSQLRestServerDB.Create(Model,'test.db3');
    DataBase.DB.Synchronous := smOff;
    Server := TSQLite3HttpServer.Create('888',[DataBase],'+');
    fRunConsole := fRunConsole+'using '+Server.HttpServer.ClassName;
    Database.NoAJAXJSON := true; // expect not expanded JSON from now on
  except
    on E: Exception do
      Check(false,E.Message);
  end;
end;
................................................................................
procedure TTestClientServerAccess.HttpClientKeepAlive;
begin
  ClientTest;
end;

procedure TTestClientServerAccess.HttpClientMultiConnect;
begin
  (Client as TSQLite3HttpClientGeneric).KeepAliveMS := 0;
  ClientTest;
end;

{
procedure TTestClientServerAccess.HttpClientMultiConnectDelphi;
begin
  if (self=nil) or (Server=nil) then
    exit; // if already Delphi code, nothing to test
  (Client as TSQLite3HttpClientGeneric).KeepAliveMS := 0;
  ClientTest;
end;

procedure TTestClientServerAccess.HttpClientKeepAliveDelphi;
begin
  if (self=nil) or (Server=nil) or (Server.HttpServer is THttpServer) then
    exit; // if already Delphi code, nothing to test
  Server.Free;
  Server := TSQLite3HttpServer.Create('888',[DataBase],'+',true);
  (Client as TSQLite3HttpClientGeneric).KeepAliveMS := 10000;
  ClientTest;
end;
}

procedure TTestClientServerAccess.NamedPipeAccess;
begin
  Check(DataBase.ExportServerNamedPipe('test'));







|

|
|
|
|







 







|







 







|













|







 







|








|









|




|


|







 







|







 







|




|






|







 







|







 







|


|
|
|







 







|







 







|







 







|








|








|
|







107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
...
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
...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
....
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
....
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
....
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
....
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
  SynDB,
{$endif}
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}
  SynSQLite3,
{$ifndef LVCL}
  SynDBSQLite3,
  mORMotDB,
{$endif}
  mORMot,
  mORMotSQLite3,
  mORMotHttpServer,
  mORMotHttpClient,
{$endif}
{$endif}
{$endif}
  SynCommons;

{ ************ Unit-Testing classes and functions }

................................................................................
    /// low-level TSynValidate classes
    procedure _TSynValidate;
    /// low-level TSynLogFile class
    procedure _TSynLogFile;
  end;

  /// this test case will test most low-level functions, classes and types
  // defined and implemented in the mORMot.pas unit
  TTestLowLevelTypes = class(TSynTestCase)
  published
{$ifndef DELPHI5OROLDER}
    /// some low-level RTTI access
    // - especially the field type retrieval from published properties
    procedure RTTI;
{$endif}
................................................................................
    procedure UrlEncoding;
    /// some low-level JSON encoding/decoding
    procedure EncodeDecodeJSON;
  end;

{$ifndef DELPHI5OROLDER}
  /// this test case will test some generic classes
  // defined and implemented in the mORMot.pas unit
  TTestBasicClasses = class(TSynTestCase)
  published
    /// test the TSQLRecord class
    // - especially SQL auto generation, or JSON export/import
    procedure _TSQLRecord;
    /// test the digital signature of records
    procedure _TSQLRecordSigned;
    /// test the TSQLModel class
    procedure _TSQLModel;
  end;

  // a record mapping used in the test classes of the framework
  // - this class can be used for debugging purposes, with the database
  // created by TTestFileBased in mORMotSQLite3.pas
  // - this class will use 'People' as a table name
  TSQLRecordPeople = class(TSQLRecord)
  private
    fData: TSQLRawBlob;
    fFirstName: RawUTF8;
    fLastName: RawUTF8;
    fYearOfBirth: integer;
................................................................................
{$endif}

{$ifndef FPC}
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}
type
  /// this test case will test most functions, classes and types defined and
  // implemented in the mORMotSQLite3 unit, i.e. the SQLite3 engine itself,
  // used as a HTTP/1.1 server and client
  // - test a HTTP/1.1 server and client on the port 888 of the local machine
  // - require the 'test.db3' SQLite3 database file, as created by TTestFileBased
  TTestClientServerAccess = class(TSynTestCase)
  protected
    { these values are used internaly by the published methods below }
    Model: TSQLModel;
    DataBase: TSQLRestServerDB;
    Server: TSQLHttpServer;
    Client: TSQLRestClient;
    /// perform the tests of the current Client instance
    procedure ClientTest;
  public
    /// release used instances (e.g. http server) and memory
    destructor Destroy; override;
    /// this could be called as administrator for THttpApiServer to work
    class function RegisterAddUrl(OnlyDelete: boolean): string;
  published
    /// initialize a TSQLHttpServer instance
    // - uses the 'test.db3' SQLite3 database file generated by TTestSQLite3Engine
    // - creates and validates a HTTP/1.1 server on the port 888 of the local
    // machine, using the THttpApiServer (using kernel mode http.sys) class
    // if available
    procedure _TSQLHttpServer;
    /// validate the HTTP/1.1 client implementation
    // - by using a request of all records data
    procedure _TSQLHttpClient;
    /// validate the HTTP/1.1 client multi-query implementation with one
    // connection for the all queries
    // - this method keep alive the HTTP connection, so is somewhat faster
    // - it runs 1000 remote SQL queries, and check the JSON data retrieved
    // - the time elapsed for this step is computed, and displayed on the report
    procedure HttpClientKeepAlive;
    /// validate the HTTP/1.1 client multi-query implementation with one
................................................................................
    // communicating
    // - it then runs 1000 remote SQL queries, and check the JSON data retrieved
    // - the time elapsed for this step is computed, and displayed on the report
    procedure DirectInProcessAccess;
  end;

  /// a parent test case which will test most functions, classes and types defined
  // and implemented in the mORMotSQLite3 unit, i.e. the SQLite3 engine itself
  // - it should not be called directly, but through TTestFileBased,
  // TTestMemoryBased and TTestMemoryBased children
  TTestSQLite3Engine = class(TSynTestCase)
  protected
    { these values are used internaly by the published methods below }
    TempFileName: TFileName;
    IsMemory: boolean;
................................................................................
    // arrays published properties handling
    // - also test FTS implementation if INCLUDE_FTS3 conditional is defined
    // - test dynamic tables
    procedure _TSQLRestClientDB;
  end;

  /// this test case will test most functions, classes and types defined and
  // implemented in the mORMotSQLite3 unit, i.e. the SQLite3 engine itself,
  // with a file-based approach
  TTestFileBased = class(TTestSQLite3Engine);

  /// this test case will test most functions, classes and types defined and
  // implemented in the mORMotSQLite3 unit, i.e. the SQLite3 engine itself,
  // with a memory-based approach
  // - this class will also test the TSQLRestServerStatic class, and its
  // 100% Delphi simple database engine
  TTestMemoryBased = class(TTestSQLite3Engine);

  /// this test case will test most functions, classes and types defined and
  // implemented in the mORMotSQLite3 unit, i.e. the SQLite3 engine itself,
  // with a file-based approach
  // - purpose of this class is to test Write-Ahead Logging for the database
  TTestFileBasedWAL = class(TTestFileBased);

  /// this class defined two published methods of type TSQLRestServerCallBack in
  //  order to test the Server-Side ModelRoot/TableName/ID/MethodName RESTful model
  TSQLRestServerTest = class(TSQLRestServerDB)
................................................................................
        named A and B, as in the public TSQLRecordPeople.Sum() method,
        which implements the Client-Side of this service
      - Table nor ID are never used here }
    function Sum(var aParams: TSQLRestServerCallBackParams): Integer;
  end;

{$ifndef LVCL}
  /// a test case which will test most external DB functions of the mORMotDB unit
  // - the external DB will be in fact a SynDBSQLite3 instance, expecting a
  // test.db3 SQlite3 file available in the current directory, populated with
  // some TSQLRecordPeople rows
  // - note that SQL statement caching at SQLite3 engine level makes those test
  // 2 times faster
  TTestExternalDatabase = class(TSynTestCase)
  protected
................................................................................
{$ifndef FPC}
{$ifndef DELPHI5OROLDER}
{$ifndef CPU64}

{ TTestClientServerAccess }

{$WARN SYMBOL_PLATFORM OFF}
procedure TTestClientServerAccess._TSQLHttpClient;
var Resp: TSQLTable;
begin
  Client := TSQLHttpClient.Create('127.0.0.1','888',Model);
  if (DebugHook<>0) and Client.InheritsFrom(TSQLHttpClientWinSock) then
    TSQLHttpClientWinSock(Client).Socket.TimeOut := 60000; // 1 minute time out
  Resp := Client.List([TSQLRecordPeople],'*');
  if CheckFailed(Resp<>nil) then
    exit;
  try
    Check(Resp.InheritsFrom(TSQLTableJSON));
    Check(Hash32(TSQLTableJSON(Resp).PrivateInternalCopy)=$352A6CCA);
  finally
................................................................................
{$WARN SYMBOL_PLATFORM ON}

class function TTestClientServerAccess.RegisterAddUrl(OnlyDelete: boolean): string;
begin
  result := THttpApiServer.AddUrlAuthorize('root','888',false,'+',OnlyDelete);
end;

procedure TTestClientServerAccess._TSQLHttpServer;
const
  MIMES: array[0..37] of TFileName = (
   'png','image/png',
   'PNg','image/png',
   'gif','image/gif',
   'tif','image/tiff',
   'tiff','image/tiff',
................................................................................
    Check(GetMimeContentType(nil,0,'toto.'+MIMES[i*2])=StringToAnsi7(MIMES[i*2+1]),MIMES[i*2]);
  Model := TSQLModel.Create([TSQLRecordPeople],'root');
  Check(Model<>nil);
  Check(Model.GetTableIndex('people')>=0);
  try
    DataBase := TSQLRestServerDB.Create(Model,'test.db3');
    DataBase.DB.Synchronous := smOff;
    Server := TSQLHttpServer.Create('888',[DataBase],'+');
    fRunConsole := fRunConsole+'using '+Server.HttpServer.ClassName;
    Database.NoAJAXJSON := true; // expect not expanded JSON from now on
  except
    on E: Exception do
      Check(false,E.Message);
  end;
end;
................................................................................
procedure TTestClientServerAccess.HttpClientKeepAlive;
begin
  ClientTest;
end;

procedure TTestClientServerAccess.HttpClientMultiConnect;
begin
  (Client as TSQLHttpClientGeneric).KeepAliveMS := 0;
  ClientTest;
end;

{
procedure TTestClientServerAccess.HttpClientMultiConnectDelphi;
begin
  if (self=nil) or (Server=nil) then
    exit; // if already Delphi code, nothing to test
  (Client as TSQLHttpClientGeneric).KeepAliveMS := 0;
  ClientTest;
end;

procedure TTestClientServerAccess.HttpClientKeepAliveDelphi;
begin
  if (self=nil) or (Server=nil) or (Server.HttpServer is THttpServer) then
    exit; // if already Delphi code, nothing to test
  Server.Free;
  Server := TSQLHttpServer.Create('888',[DataBase],'+',true);
  (Client as TSQLHttpClientGeneric).KeepAliveMS := 10000;
  ClientTest;
end;
}

procedure TTestClientServerAccess.NamedPipeAccess;
begin
  Check(DataBase.ExportServerNamedPipe('test'));

Changes to SynWinSock.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
17
18
19
20
21
22
23
24
25
26


27
28

29
30
31
32
33
34
35
36
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/// low level access to network Sockets for the Win32 platform
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.6
unit SynWinSock;

{
    This file is part of Synopse SQLite3 database framework.

    Synopse SQLite3 database framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3 database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.



  Portions created by the Initial Developer are Copyright (C) 2012

  the Initial Developer. All Rights Reserved.

  Contributor(s):
  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
................................................................................
  ***** END LICENSE BLOCK *****



     Low level access to network Sockets
    *************************************
    
   The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).
   Portions created by Lukas Gebauer are Copyright (c)2003.
   All Rights Reserved.

  Contributor(s):
   - Arnaud Bouchez, Jan 2009, for SynCrtSock: see http://synopse.info
     Delphi 2009/2010 compatibility (Jan 2010): the WinSock library
       expects Ansi encoded parameters
}

{.$DEFINE WINSOCK1}


|



|

|







 







|

|
>
>

<
>
|







 







<
<
<
<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
17
18
19
20
21
22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
..
46
47
48
49
50
51
52




53
54
55
56
57
58
59
/// low level access to network Sockets for the Win32 platform
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynWinSock;

{
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synapse library.

  The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).
  Portions created by Lukas Gebauer are Copyright (C) 2003.
  All Rights Reserved.


  Portions created by Arnaud Bouchez are Copyright (C) 2012 Arnaud Bouchez.
  All Rights Reserved.

  Contributor(s):
  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
................................................................................
  ***** END LICENSE BLOCK *****



     Low level access to network Sockets
    *************************************
    




  Contributor(s):
   - Arnaud Bouchez, Jan 2009, for SynCrtSock: see http://synopse.info
     Delphi 2009/2010 compatibility (Jan 2010): the WinSock library
       expects Ansi encoded parameters
}

{.$DEFINE WINSOCK1}

Changes to SynZip.pas.

1
2
3
4
5
6
7
8
9
10
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/// low-level access to ZLib compression (1.2.5 engine version)
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SynZip;

{
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3/mORMot database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):


|







 







|







1
2
3
4
5
6
7
8
9
10
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/// low-level access to ZLib compression (1.2.5 engine version)
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynZip;

{
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):

Changes to SynZipFiles.pas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
....
2050
2051
2052
2053
2054
2055
2056
2057
/// high-level access to .zip archive file compression
// - this unit is a part of the freeware Synopse SQLite3 database framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.13
unit SynZipFiles;

(*
    This file is part of Synopse SQLite3 database framework.

    Synopse SQLite3 database framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse SQLite3 database framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  end;
end;

initialization
  fillchar(BlobDataNull,sizeof(TBlobData),0);

end.


|
|



|

|







 







|







 







<
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
....
2050
2051
2052
2053
2054
2055
2056

/// high-level access to .zip archive file compression
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynZipFiles;

(*
    This file is part of Synopse framework.

    Synopse framework. Copyright (C) 2012 Arnaud Bouchez
      Synopse Informatique - http://synopse.info

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
................................................................................
  the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Original Code is Synopse framework.

  The Initial Developer of the Original Code is Arnaud Bouchez.

  Portions created by the Initial Developer are Copyright (C) 2012
  the Initial Developer. All Rights Reserved.

  Contributor(s):
................................................................................
  end;
end;

initialization
  fillchar(BlobDataNull,sizeof(TBlobData),0);

end.