support for delphi 11.1

This commit is contained in:
Razor12911 2022-05-13 13:05:10 +02:00
parent 8ceccef928
commit 39fb5ae479
167 changed files with 8914 additions and 3205 deletions

View File

@ -2,7 +2,7 @@
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -21,7 +21,7 @@
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit SynCrossPlatformCrypto;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynCrossPlatformCrypto;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit SynCrossPlatformJSON;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynCrossPlatformJSON;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -1682,7 +1682,7 @@ begin
VKind := jvObject else
if VKind<>jvObject then
raise EJSONException.CreateFmt('AddNameValue(%s) over array',[aName]);
if VCount<=length(Values) then begin
if VCount=length(Values) then begin
SetLength(Values,VCount+VCount shr 3+32);
SetLength(Names,VCount+VCount shr 3+32);
end;
@ -1697,7 +1697,7 @@ begin
VKind := jvArray else
if VKind<>jvArray then
raise EJSONException.Create('AddValue() over object');
if VCount<=length(Values) then
if VCount=length(Values) then
SetLength(Values,VCount+VCount shr 3+32);
Values[VCount] := aValue;
inc(VCount);

View File

@ -6,7 +6,7 @@ unit SynCrossPlatformREST;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynCrossPlatformREST;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -253,8 +253,8 @@ type
/// a set of published property Kind
TSQLFieldKinds = set of TSQLFieldKind;
{ TODO: TID should be a string since number is limited to 53-bit in JavaScript
-> define and use an explicit Int52 type for SMS }
{ Should TID be a string since number is limited to 53-bit in JavaScript?
-> or define and use an explicit Int52 type for SMS? }
/// the TSQLRecord primary key is a 64 bit integer
TID = {$ifndef ISDWS}type{$endif} Int64;
@ -2895,7 +2895,7 @@ begin
end;
/// marshall {result:...,id:...} and {result:...} body answers
function CallGetResult(const aCall: TSQLRestURIParams; var outID: integer): variant;
function CallGetResult(const aCall: TSQLRestURIParams; var outID: TID): variant;
{$ifndef ISSMS}
var doc: TJSONVariantData;
jsonres: string;
@ -2922,7 +2922,7 @@ end;
function TSQLRestClientURI.CallBackGetResult(const aMethodName: string;
const aNameValueParameters: array of const; aTable: TSQLRecordClass; aID: TID): string;
var Call: TSQLRestURIParams;
dummyID: integer;
dummyID: TID;
begin
CallBackGet(aMethodName,aNameValueParameters,Call,aTable,aID);
result := CallGetResult(Call,dummyID);
@ -3038,7 +3038,7 @@ begin
onError(self);
exit;
end;
var outID: integer;
var outID: TID;
var result := CallGetResult(Call,outID); // from {result:...,id:...}
if VarIsValidRef(result) then begin
if (aCaller.fInstanceImplementation=sicClientDriven) and (outID<>0) then
@ -3067,7 +3067,7 @@ function TSQLRestClientURI.CallRemoteServiceSynch(aCaller: TServiceClientAbstrac
const aInputParams: array of variant; aReturnsCustomAnswer: boolean): TVariantDynArray;
var Call: TSQLRestURIParams;
outResult: variant;
outID: integer;
outID: TID;
procedure RaiseError;
begin
raise EServiceException.CreateFmt('Error calling %s.%s - returned status %d',
@ -3119,7 +3119,8 @@ var Call: TSQLRestURIParams;
result: variant;
bodyerror: string;
arr: PJSONVariantData;
i,outID: integer;
i: integer;
outID: TID;
begin
params.Init;
for i := 0 to high(aInputParams) do
@ -3671,7 +3672,7 @@ end;
constructor TServiceClientAbstract.Create(aClient: TSQLRestClientURI);
var Call: TSQLRestURIParams; // manual synchronous call
dummyID: integer;
dummyID: TID;
result: variant;
contract: string;
begin

View File

@ -6,7 +6,7 @@ unit SynCrossPlatformSpecific;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynCrossPlatformSpecific;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -8,7 +8,7 @@ interface
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -27,7 +27,7 @@ interface
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -86,10 +86,10 @@ type
{$ifdef FPC}
PBytes = PAnsiChar;
{$else}
PtrUInt = {$ifdef CPUX64} NativeUInt {$else} cardinal {$endif};
PtrUInt = {$ifdef UNICODE} NativeUInt {$else} cardinal {$endif};
TBytes = array[0..maxInt-1] of byte;
PBytes = ^TBytes;
{$endif}
{$endif FPC}
function SynLZcomp(src: pointer; size: cardinal; dst: pointer): cardinal;
var dst_beg, // initial dst value
@ -190,7 +190,7 @@ function SynLZdecomp(src: pointer; size: cardinal; dst: pointer): cardinal;
var last_hashed, // initial src and dst value
src_end: PtrUInt;
CW, CWbit: cardinal;
v, t, h, o: cardinal;
v, t, h, o: PtrUInt;
i: integer;
offset: array[0..4095] of PtrUInt; // 16KB hashing code
label nextCW;

View File

@ -6,7 +6,7 @@ unit SynCrossPlatformTests;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynCrossPlatformTests;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ unit dddDomAuthInterfaces;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddDomAuthInterfaces;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit dddDomCountry;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddDomCountry;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -1,4 +1,4 @@
unit DomUserInterfaces;
unit dddDomEmailInterfaces;
interface

View File

@ -6,7 +6,7 @@ unit dddDomUserCQRS;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddDomUserCQRS;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit dddDomUserInterfaces;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddDomUserInterfaces;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit dddDomUserTypes;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddDomUserTypes;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit dddInfraApps;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddInfraApps;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -595,7 +595,8 @@ type
procedure ExecuteDisconnect;
procedure ExecuteDisconnectAfterError;
procedure ExecuteSocket;
function TrySend(const aFrame: RawByteString; ImmediateDisconnectAfterError: boolean = true): Boolean; virtual;
function TrySend(const aFrame: RawByteString;
ImmediateDisconnectAfterError: boolean = true): Boolean; virtual;
// inherited classes could override those methods for process customization
procedure InternalExecuteConnected; virtual;
procedure InternalExecuteDisconnect; virtual;

View File

@ -6,7 +6,7 @@ unit dddInfraAuthRest;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddInfraAuthRest;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit dddInfraEmail;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddInfraEmail;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit dddInfraEmailer;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddInfraEmailer;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit dddInfraRepoUser;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddInfraRepoUser;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit dddInfraSettings;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit dddInfraSettings;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -28,7 +28,7 @@ HtmlSideBar=Overview/Meet the mORMot:SOURCE,Download/How to install:TITL_113,API
; the sidebar first links, for html export
{\b Document License}
{\i Synopse mORMot Framework Documentation}.\line Copyright (C) 2008-2020 Arnaud Bouchez.\line Synopse Informatique - @https://synopse.info
{\i Synopse mORMot Framework Documentation}.\line Copyright (C) 2008-2022 Arnaud Bouchez.\line Synopse Informatique - @https://synopse.info
The {\i Synopse mORMot Framework Source Code} is licensed under GPL / LGPL / MPL licensing terms, free to be included in any application.
;This documentation has been generated using {\i Synopse SynProject} - @https://synopse.info/fossil/wiki?name=SynProject
;This document is a 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.
@ -136,7 +136,7 @@ The main approach of this framework is to avoid @*RAD@ in the development of pro
: Expected Use
Any application which need moderate database usage (up to some GB of data) with easy setup and administration, together with a secure @*ACID@ behavior in a Client-Server environment should consider using the {\i Synopse mORMot Framework}.
: Requirement Exceptions
This framework was developed in order to run mainly under any {\i Delphi} compiler, from version {\i Delphi} 6 to version {\i Delphi 10.3 Rio}.
This framework was developed in order to run mainly under any {\i Delphi} compiler, from version {\i Delphi} 6 to the latest Delphi version ({\i Delphi 11 Alexandria} at time of this writing).
On the {\i server side}, it targets both {\i Win32} and {\i Win64} platforms (using the 64-bit compiler included in latest {\i Delphi} XE2 and up).
For clients, in addition to those {\i Win32} / {\i Win64} platforms, you have cross-platform code generation abilities, for any {\i Delphi} or {\i @*FreePascal@} target (including {\i @*OSX@} and mobile {\i iOS} or {\i Android}), or AJAX / HTML5 clients via {\i @*Smart Mobile Studio@} - see @90@.
=[License]
@ -464,7 +464,7 @@ The {\i Synopse mORMot Framework} shall provide User Interface and Report genera
Such a ribbon-oriented interface shall be made available, in a per-table approach, and associated reports.
Here is a sample of screen content, using proprietary TMS components:
%synfiletms.png
And here is the same application compiled using only VCL components, available from {\i Delphi} 6 up to {\i Delphi 10.3 Rio}:
And here is the same application compiled using only VCL components, available from {\i Delphi} 6 up to the latest available {\i Delphi} version:
%synfilevcl.png
[SRS-DI-2.3.1]
@ -570,7 +570,7 @@ DisplayName=mORMot Framework Overview
:Synopse mORMot Overview
%IamLost.png
{\i Synopse mORMot} is an Open Source @*Client-Server@ @*ORM@ @*SOA@ @*MVC@ framework for {\i Delphi} 6 up to {\i Delphi 10.3 Rio} and @*FPC@, targeting {\i Win/@*Linux@} for the server, and any platform for clients (including mobile or AJAX).
{\i Synopse mORMot} is an Open Source @*Client-Server@ @*ORM@ @*SOA@ @*MVC@ framework for {\i Delphi} 6 up to the latest available {\i Delphi} version and @*FPC@ 3.2, targeting {\i Win/@*Linux@} for the server, and any platform for clients (including mobile or AJAX).
The main features of {\i mORMot} are therefore:
- {\i ORM/ODM}: objects persistence on almost any database (SQL or NoSQL);
- {\i SOA}: organize your business logic into @*REST@ services;
@ -708,7 +708,7 @@ At first, some points can be highlighted, which make this framework distinct to
- More than 1800 pages of documentation;
- {\i Delphi}, {\i FreePascal}, mobile and @*AJAX@ clients can share the same server, and ORM/SOA client access code can be generated on request for any kind of application - see @86@;
- Full source code provided - so you can enhance it to fulfill any need;
- Works from {\i Delphi} 6 up to {\i Delphi 10.3 Rio} and FPC 2.6.4/2.7.1/3.x, truly Unicode (uses @*UTF-8@ encoding in its kernel, just like JSON), with any version of {\i Delphi} (no need to upgrade your IDE).
- Works from {\i Delphi} 6 up to the latest available {\i Delphi} version and FPC 3.2.x, truly Unicode (uses @*UTF-8@ encoding in its kernel, just like JSON), with any version of {\i Delphi} (no need to upgrade your IDE).
\page
: Benefits
As you can see from the previous section, {\i mORMot} provides a comprehensive set of features that can help you to manage your crosscutting concerns though a reusable set of components and core functionality.
@ -1364,7 +1364,7 @@ In the following next paragraphs, we'll comment some main features of the lowest
- {\f1\fs20 @*TDocVariant@} custom {\f1\fs20 variant} type for dynamic schema-less {\i object} or {\i array} storage.
Other shared features available in {\f1\fs20 SynTests.pas} and {\f1\fs20 SynLog.pas} will be detailed later, i.e. @*Test@ing and @*Log@ging - see @12@.
:32 Unicode and UTF-8
Our {\i mORMot} Framework has 100% UNICODE compatibility, that is compilation under {\i Delphi} 2009 and up (including latest {\i Delphi 10.3 Rio} revision). 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 versions, i.e. from {\i Delphi} 6 up to {\i Delphi} 2007.
Our {\i mORMot} Framework has 100% UNICODE compatibility, that is compilation under {\i Delphi} 2009 and up (including the latest available {\i Delphi} version). 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 versions, i.e. from {\i Delphi} 6 up to {\i Delphi} 2007.
From its core to its uppermost features, our framework is natively @**UTF-8@, which is the de-facto character encoding for @*JSON@, {\i @*SQLite3@}, and most supported database engines. This allows our code to offer fast streaming/parsing in a @*SAX@-like mode, avoiding any conversion between encodings from the storage layer to your business logic. We also needed to establish a secure way to use strings, in order to handle all versions of {\i Delphi} (even pre-Unicode versions, especially the {\i Delphi} 7 version we like so much), and provide compatibility with the {\i @*FreePascal@ Compiler}. This consistency allows to circumvent any RTL bug or limitation, and ease long-term support of your project.
Some string types have been defined, and used in the code for best cross-compiler efficiency:
- {\f1\fs20 @**RawUTF8@} is used for every internal data usage, since both {\i SQLite3} and JSON do expect UTF-8 encoding;
@ -1463,7 +1463,7 @@ Here is how those new methods work:
! if GroupA.Find(v)<0 then // fast binary search
! ShowMessage('Error: 1500 not found!');
Some unique methods like {\f1\fs20 Slice, Reverse} or {\f1\fs20 AddArray} are also available, and mimic well-known Python methods.
Still closer to the generic paradigm, working for {\i Delphi} 6 up to {\i Delphi 10.3 Rio}, without the need of the slow enhanced RTTI, nor the executable size overhead and compilation issues of generics...
Still closer to the generic paradigm, working for {\i Delphi} 6 up to the latest available {\i Delphi} version, without the need of the slow enhanced RTTI, nor the executable size overhead and compilation issues of generics...
: Capacity handling via an external Count
One common speed issue with the default usage of {\f1\fs20 TDynArray} is that the internal memory buffer is reallocated when you change its length, just like a regular {\i Delphi} {\i dynamic array}.
That is, whenever you call {\f1\fs20 Add} or {\f1\fs20 Delete} methods, an internal call to {\f1\fs20 SetLength(DynArrayVariable)} is performed. This could be slow, because it always executes some extra code, including a call to {\f1\fs20 ReallocMem}.
@ -1829,7 +1829,7 @@ When working with complex documents, e.g. with @*BSON@ / {\i @*MongoDB@} documen
: Advanced TDocVariant process
:194 Number values options
By default, {\f1\fs20 TDocVariantData} will only recognize {\f1\fs20 integer}, {\f1\fs20 Int64} and {\f1\fs20 currency} - see @33@ - as number values. Any floating point value which may not be translated to/from @*JSON@ textual representation safely will be stored as a JSON string, i.e. if it does match an integer or up to 4 fixed decimals, with 64-bit precision. We stated that JSON serialization should be conservative, i.e. serializing then unserializing (or the other way round) should return the very same value; parsing JSON is a matter of (difficult) choices - see @http://seriot.ch/parsing_json.php#5 - and we choose to be paranoid and not loose information by default.
You can set the {\f1\fs20 @*dvoAllowDoubleValue@} option to {\f1\fs20 TDocVariantData}, so that such floating-point numbers will be recognized and stored as {\f1\fs20 @*double@}. In this case, only {\f1\fs20 varDouble} storage will be used for the {\f1\fs20 variant} values, i.e. 64-bit IEEE 754 {\f1\fs20 double} values, handling 5.0 x 10^-324 .. 1.7 x 10^308 range. With such floating-point values, you may loose precision and digits during the JSON serialization process: this is why it is not enabled by default.
You can use the {\f1\fs20 @*_JsonFastFloat()@} wrapper or set the {\f1\fs20 @**dvoAllowDoubleValue@} option to {\f1\fs20 TDocVariantData}, so that such floating-point numbers will be recognized and stored as {\f1\fs20 @**double@}. In this case, only {\f1\fs20 varDouble} storage will be used for the {\f1\fs20 variant} values, i.e. 64-bit IEEE 754 {\f1\fs20 double} values, handling 5.0 x 10^-324 .. 1.7 x 10^308 range. With such floating-point values, you may loose precision and digits during the JSON serialization process: this is why it is not enabled by default.
Also note that some JSON engines do not support 64-bit integer numbers. For instance, {\f1\fs20 @*JavaScript@} engines handle only up to @*53-bit@ of information without precision loss (called the {\i significand} bits), due to their internal storage as a 8 bytes IEEE 754 container. In some cases, it is safest to use JSON string representation of such numbers, as is done with the {\f1\fs20 woIDAsIDstr} value of {\f1\fs20 TTextWriterWriteObjectOption} for safe serialization of {\f1\fs20 TSQLRecord.ID} ORM values.
If you want to work with high-precision floating point numbers, consider using {\f1\fs20 @*TDecimal128@} values, as implemented in {\f1\fs20 SynMongoDB.pas}, which supports 128-bit high precision decimal, as defined by the {\i IEEE 754-2008 128-bit decimal floating point} standard, and handled in {\i MongoDB} 3.4+. Their conversion to/from text - therefore to/from JSON - won't loose nor round any digit, as soon as the value fits in its 128-bit storage.
: Object or array document creation options
@ -4610,7 +4610,7 @@ In the above query expression, the {\f1\fs20 rank()} function is used over the d
In any database, there is a need to define how column data is to be compared. It is needed for proper search and ordering of the data. This is the purpose of so-called {\i @**collation@s}.
By default, when {\i SQLite} compares two strings, it uses a collating sequence or collating function (two words for the same thing) to determine which string is greater or if the two strings are equal. {\i SQLite} has three built-in collating functions: BINARY, NOCASE, and RTRIM:
- BINARY - Compares string data using {\f1\fs20 memcmp()}, regardless of text encoding.
- NOCASE - The same as binary, except the 26 upper case characters of ASCII are folded to their lower case equivalents before the comparison is performed. Note that only ASCII characters are case folded. Plain {\i SQLite} does not attempt to do full @*Unicode@ case folding due to the size of the tables required - but you could use {\i mORMot}'s SYSTEMNOCASE or WIN32CASE/WIN32NOCASE custom collations for enhanced case folding support (see below);
- NOCASE - The same as binary, except the 26 upper case characters of ASCII are folded to their lower case equivalents before the comparison is performed. Note that only ASCII characters are case folded. Plain {\i SQLite} does not attempt to do full @*Unicode@ case folding due to the size of the tables required - but you could use {\i mORMot}'s SYSTEMNOCASE, or WIN32CASE/WIN32NOCASE custom collations for enhanced case folding support (see below);
- RTRIM - The same as binary, except that trailing space characters are ignored.
In the {\i mORMot} ORM, we defined some additional kind of collations, via some internal calls to the {\f1\fs20 sqlite3_create_collation()} API:
|%25%60
@ -4632,6 +4632,7 @@ The following collations are therefore available when using {\i SQLite3} within
|NOCASE|Default ASCII 7 bit comparison
|RTRIM|Default {\f1\fs20 memcmp()} comparison with right trim
|SYSTEMNOCASE|{\i mORMot}'s Win-1252 8 bit comparison
;|UNICODENOCASE|{\i mORMot}'s Unicode 10.0 comparison
|ISO8601|{\i mORMot}'s date/time comparison
|WIN32CASE|{\i mORMot}'s comparison using case-insensitive Windows API
|WIN32NOCASE|{\i mORMot}'s comparison using not case-insensitive Windows API
@ -4639,17 +4640,17 @@ The following collations are therefore available when using {\i SQLite3} within
Note that WIN32CASE/WIN32NOCASE will be slower than the others, but will handle properly any kind of complex scripting. For instance, if you want to use the Unicode-ready Windows API at database level, you can set for each database model:
! aModel.SetCustomCollationForAll(sftUTF8Text,'WIN32CASE');
! aModel.SetCustomCollationForAll(sftDateTime,'NOCASE');
On non-Windows platform, it will either use the system ICU library (if available), or fallback to the FPC RTL with temporary {\f1\fs20 UnicodeString} values - which requires to include `cwstrings` in your project uses clause. Note that depending on the library used, the results may not be consistent: so if you move a {\i SQLite3} database file e.g. from a Windows system to a Linux system with WIN32CASE collation, you should better regenerate all your indexes!
If you use non-default collations (i.e. SYSTEMNOCASE/ISO8601/WIN32CASE/WIN32NOCASE), you may have trouble running requests with "plain" {\i SQLite3} tools. But you can use our {\f1\fs20 @*SynDBExplorer@} safely, since it will declare all the above collations.
When using external databases - see @27@, if the content is retrieved directly from the database driver and by-passes the virtual table mechanism - see @20@, returned data may not match your expectations according to the custom collations: you will need to customize the external tables definition by hand, with the proper SQL statement of each external DB engine.
Note that {\i @*mORMot 2@} offers a new UNICODENOCASE collation, which follows Unicode 10.0 without any Windows or ICU API call, so is consistent on all systems - and is also faster.
: REGEXP operator
Our {\i SQLite3} engine can use {\i @**regular expression@} within its SQL queries, by enabling the {\f1\fs20 @**REGEXP@} operator in addition to standard SQL operators ({\f1\fs20 = == != <> IS IN LIKE GLOB MATCH}). It will use the Open Source PCRE library to perform the queries.
In order to enable the operator, you should include unit {\f1\fs20 SynSQLite3RegEx.pas} to your uses clause, and register the {\f1\fs20 RegExp()} SQL function to a given {\i SQLite3} database instance, as such:
!uses SynCommons, mORmot, mORMotSQLite3,
!! SynSQLite3RegEx;
! ...
Our {\i SQLite3} engine can use {\i @**regular expression@} within its SQL queries, by enabling the {\f1\fs20 @**REGEXP@} operator in addition to standard SQL operators ({\f1\fs20 = == != <> IS IN LIKE GLOB MATCH}).
: Default REGEXP Engine
By default, and since mORMot 1.18.6218 (25 January 2021), our static {\i SQlite3} engine includes a compact and efficient enough C extension, as available from the official {\i SQLite3} project source code tree. It is included with the official amalgamation file during our compilation phase.
So you don't need to do anything to be able to use the REGEX operator in your queries:
!Server := TSQLRestServerDB.Create(Model,'test.db3');
!try
!! CreateRegExpFunction(Server.DB.DB);
! with TSQLRecordPeople.CreateAndFillPrepare(Client,
!! 'FirstName REGEXP ?',['\bFinley\b']) do
! try
@ -4667,6 +4668,20 @@ The above code will execute the following SQL statement (with a prepared paramet
! SELECT * from People WHERE Firstname REGEXP '\bFinley\b';
That is, it will find all objects where {\f1\fs20 TSQLRecordPeople.FirstName} will contain the {\f1\fs20 'Finley'} word - in a regular expression, {\f1\fs20 \\b} defines a word {\f1\fs20 b}oundary search.
In fact, the {\f1\fs20 REGEXP} operator is a special syntax for the {\f1\fs20 regexp()} user function. No {\f1\fs20 regexp()} user function is defined by default and so use of the {\f1\fs20 REGEXP} operator will normally result in an error message. Calling {\f1\fs20 CreateRegExFunction()} for a given connection will add a SQL function named "{\f1\fs20 regexp()}" at run-time, which will be called in order to implement the {\f1\fs20 REGEXP} operator.
: PCRE REGEXP Engine
If you want to use the Open Source PCRE library to perform the searches, instead of this default C extension, you should include the {\f1\fs20 SynSQLite3RegEx.pas} unit to your uses clause, and register the {\f1\fs20 RegExp()} SQL function to a given {\i SQLite3} database instance, as such:
!uses SynCommons, mORmot, mORMotSQLite3,
!! SynSQLite3RegEx;
! ...
!Server := TSQLRestServerDB.Create(Model,'test.db3');
!try
!! CreateRegExpFunction(Server.DB.DB);
! with TSQLRecordPeople.CreateAndFillPrepare(Client,
!! 'FirstName REGEXP ?',['\bFinley\b']) do
! try
! while FillOne do begin
! Check(LastName='Morse');
! ...
It will use the statically linked PCRE library as available since {\i Delphi} XE, or will rely on the {\f1\fs20 PCRE.pas} wrapper unit as published at @http://www.regular-expressions.info/download/TPerlRegEx.zip for older versions of {\i Delphi}.
This unit will call directly the @*UTF-8@ API of the PCRE library, and maintain a per-connection cache of compiled regular expressions to ensure the best performance possible.
:60 ACID and speed
@ -4752,7 +4767,8 @@ Note that the virtual table module name is retrieved from the class name. For in
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 {\i Delphi} methods:
Here is how the {\f1\fs20 TSQLVirtualTableLog} class type is defined, which will implement a @*Virtual Table@ module named "{\f1\fs20 Log}". Note that the {\i SQLite3} virtual table module name will be computed from the class name, trimming its first characters, e.g. {\f1\fs20 TSQLVirtualTable{\b Log}} will trim trailing {\f1\fs20 TSQLVirtualTable} and define a {\f1\fs20 'Log'} virtual module.
Adding a new module is just made by overriding some {\i Delphi} methods:
! TSQLVirtualTableLog = class(TSQLVirtualTable)
! protected
! fLogFile: TSynLogFile;
@ -7431,7 +7447,7 @@ In practice, for your project, you will have several possibilities to create a C
|SOA Interfaces|RPC REST|RPC
|SOA Methods|Full REST/HTTP|Verbose
|MVC Web|Web site + AJAX|HTML-oriented
|ORM REST|Tests or internal use|Security/design flows
|ORM REST|Tests or internal use|Security/design flaws
|%
In a nutshell,
- {\i SOA Interfaces} - see @63@ - is the preferred way to build both public and private services: both client and server code will be defined from {\f1\fs20 interface} types, including sessions management, stubbing/mocking, documentation generation, and security features.
@ -9668,7 +9684,7 @@ If you compare with existing mocking frameworks, even in other languages / platf
- Most common parameters and results can be defined as simple {\f1\fs20 array of const} in the {\i Delphi} code, or by supplying JSON arrays (needed e.g. for more complex structures like {\f1\fs20 record} values);
- Execution trace retrieval in easy to read or write text format (and not via complex "fluent" interface e.g. with {\f1\fs20 When} clauses);
- Auto-release of the {\f1\fs20 TInterfaceStub TInterfaceMock TInterfaceMockSpy} generator instance, when the interface is no longer required, to minimize the code to type, and avoid potential memory leaks;
- Works from {\i Delphi} 6 up to {\i Delphi 10.3 Rio} - since no use of syntax sugar like generics, nor the {\f1\fs20 RTTI.pas} features;
- Works from {\i Delphi} 6 up to the latest available {\i Delphi} version - since no use of syntax sugar like generics, nor the {\f1\fs20 RTTI.pas} features;
- Very good performance (the faster {\i Delphi} mocking framework, for sure), due to very low overhead and its reuse of {\i mORMot}'s low-level interface-based services kernel using JSON serialization, which does not rely on the slow and limited {\f1\fs20 TVirtualInterface}.
: Stubbing complex return values
Just imagine that the {\f1\fs20 ForgotMyPassword} method does perform an internal test:
@ -9881,7 +9897,7 @@ Here are the key features of the current implementation of services using interf
|Server factory|You can get an implementation on the server side
|Client factory|You can get a "fake" implementation on the client side, remotely calling the server to execute the process
|Cross-platform clients|A {\i mORMot} server is able to generate cross-platform client code via a set of templates - see @86@
|Auto marshalling|The contract is transparently implemented: no additional code is needed e.g. on the client side, and will handle simple types (strings, numbers, dates, sets and enumerations) and high-level types (objects, collections, records, dynamic arrays, variants) from {\i Delphi} 6 up to {\i Delphi 10.3 Rio}
|Auto marshalling|The contract is transparently implemented: no additional code is needed e.g. on the client side, and will handle simple types (strings, numbers, dates, sets and enumerations) and high-level types (objects, collections, records, dynamic arrays, variants) from {\i Delphi} 6 up to the latest available {\i Delphi} version
|Flexible|Methods accept per-value or per-reference parameters
|Instance lifetime|An implementation class can be:\line - Created on every call,\line - Shared among all calls,\line - Shared for a particular user or group,\line - Dedicated to the thread it runs on,\line - Alive as long as the client-side interface is not released,\line - Or as long as an @*authentication@ session exists
|@*Stateless@|Following a standard request/reply pattern
@ -9983,7 +9999,7 @@ You can therefore define complex {\f1\fs20 interface} types, as such:
! /// validates ArgsInputIsOctetStream raw binary upload
! function DirectCall(const Data: TSQLRawBlob): integer;
! end;
Note how {\f1\fs20 SpecialCall} and {\f1\fs20 ComplexCall} methods have quite complex parameters definitions, including dynamic arrays, sets and records. DirectCall will use binary POST, by-passing @*Base64@ JSON encoding - see @197@. The framework will handle {\f1\fs20 const} and {\f1\fs20 var} parameters as expected, i.e. as input/output parameters, also on the client side. Any simple types of dynamic arrays (like {\f1\fs20 TIntegerDynArray}, {\f1\fs20 TRawUTF8DynArray}, or {\f1\fs20 TWideStringDynArray}) will be serialized as plain JSON arrays - the framework is able to handle any dynamic array definition, but will serialize those simple types in a more AJAX compatible way, thanks to the enhanced RTTI available since to {\i Delphi} 2010.
Note how {\f1\fs20 SpecialCall} and {\f1\fs20 ComplexCall} methods have quite complex parameters definitions, including dynamic arrays, sets and records. {\f1\fs20 DirectCall} will use binary POST, by-passing @*Base64@ JSON encoding - see @197@. The framework will handle {\f1\fs20 const} and {\f1\fs20 var} parameters as expected, i.e. as input/output parameters, also on the client side. Any simple types of dynamic arrays (like {\f1\fs20 TIntegerDynArray}, {\f1\fs20 TRawUTF8DynArray}, or {\f1\fs20 TWideStringDynArray}) will be serialized as plain JSON arrays - the framework is able to handle any dynamic array definition, but will serialize those simple types in a more AJAX compatible way, thanks to the enhanced RTTI available since to {\i Delphi} 2010.
: TPersistent / TSQLRecord parameters
As stated above, {\i mORMot} does not allow a method {\f1\fs20 function} to return a {\f1\fs20 class} instance.
That is, you can't define such a method:
@ -13659,7 +13675,7 @@ By default, the following security groups are created on a void database:
|%14%12%14%11%11%12%12%12
|\b Group|POST SQL|SELECT SQL|Auth R|Auth W|Tables R|Tables W|Services\b0
|Admin|Yes|Yes|Yes|Yes|Yes|Yes|Yes
|Supervisor|Yes|No|Yes|No|Yes|Yes|Yes
|Supervisor|No|Yes|Yes|No|Yes|Yes|Yes
|User|No|No|No|No|Yes|Yes|Yes
|Guest|No|No|No|No|Yes|No|No
|%
@ -15736,7 +15752,7 @@ And even better, testing-driven coding can be encouraged:
It could sounds like a waste of time, but such coding improve your code quality a lot, and, at least, it help you write and optimize every implementation feature.
The framework has been implemented using this approach, and provide all the tools to write tests. In addition to what other {\i Delphi} frameworks offer (e.g. {\i DUnit / DUnitX}), the {\f1\fs20 SynTests.pas} unit is very much integrated with other elements of the framework (like logging), is cross-platform and cross-compiler, and provides a complete {\i stubbing / mocking} mechanism to cover @62@.
: Involved classes in Unitary testing
The @!TSynTest,TSynTestCase,TSynTests!Lib\SynTests.pas@ unit defines two classes (both inheriting from {\f1\fs20 TSynTest}), implementing a complete Unitary testing mechanism similar to {\i DUnit}, with less code overhead, and direct interface with the framework units and requirements (@*UTF-8@ ready, code compilation from {\i Delphi} 6 up to {\i Delphi 10.3 Rio} and FPC, no external dependency).
The @!TSynTest,TSynTestCase,TSynTests!Lib\SynTests.pas@ unit defines two classes (both inheriting from {\f1\fs20 TSynTest}), implementing a complete Unitary testing mechanism similar to {\i DUnit}, with less code overhead, and direct interface with the framework units and requirements (@*UTF-8@ ready, code compilation from {\i Delphi} 6 up to the latest available {\i Delphi} version and FPC, no external dependency).
The following diagram defines this class hierarchy:
\graph HierTSynTest TSynTest classes hierarchy
\TSynTests\TSynTest
@ -15850,12 +15866,10 @@ Before any release all unitary regression tests are performed with the following
- {\i Delphi} 2007;
- {\i Delphi} 2010 (we assume that if it works with {\i Delphi} 2010, it will work with {\i Delphi} 2009, with the exception of {\f1\fs20 generic} compilation);
- {\i Delphi} XE4;
- {\i Delphi} XE6;
- {\i Delphi} XE7;
- {\i Delphi} 10 Seattle;
- {\i Delphi} 10.1 Berlin;
- {\i Delphi} 10.2 Tokyo;
- {\i Delphi} XE8;
- {\i Delphi} 10.3 Rio;
- {\i Delphi} 10.4 Sidney;
- {\i @*CrossKylix@} 3.0;
- {\i @*FPC@} 3.x - preferred is {\i 3.2 fixes}.
Target platforms are {\i Win32} and {\i Win64} for {\i Delphi} and {\i FPC}, plus {\i Linux 32/64} for {\i FPC} and {\i CrossKylix}.
@ -16223,17 +16237,17 @@ Follow these steps:
- Finally, click on the "{\i Zip Archive}" link, available at the end of the "{\i Overview}" header, right ahead to the "{\i Other Links}" title. This link will build a {\f1\fs20 .zip} archive of the complete source code and download it to your browser.
: Expected compilation platform
The framework source code tree will compile and is tested for the following platforms:
- {\i Delphi} 6 up to {\i Delphi 10.3 Rio} compiler and IDE, with {\i @*FreePascal@ Compiler} (FPC) 3.x and {\i @*Lazarus@} support;
- {\i Delphi} 6 up to the latest {\i Delphi} compiler and IDE version, with {\i @*FreePascal@ Compiler} (FPC) 3.x and {\i @*Lazarus@} support;
- Server side on Windows 32-bit and @**64-bit@ platforms (FPC or {\i Delphi} XE2 and up expected when targeting {\i Win64});
- {\i @*Linux@} 32-bit and 64-bit platform for servers using the FPC 3.2 fixes branch - now stable and tested in production since years (especially {\i @*Debian@/@*Ubuntu@} on {\f1\fs20 x86_64});
- VCL client on Win32/Win64 - GUI may be compiled optionally with third-party non Open-Source @*TMS@ Components, instead of default VCL components - see @http://www.tmssoftware.com/site/tmspack.asp
- @69@ clients on any supported platforms;
- @90@ startup with 2.1, for creating @*AJAX@ / @*JavaScript@ / HTML5 / Mobile clients.
Some part of the library (e.g. {\f1\fs20 SynCommons.pas}, {\f1\fs20 SynTests.pas}, {\f1\fs20 SynLog.pas} {\f1\fs20 SynPDF.pas} or the @27@ units) are also compatible with {\i Delphi} 5.
If you want to compile {\i mORMot} unit into @*packages@, to avoid an obfuscated {\i [DCC Error] @*E2201@ Need imported data reference ($G) to access 'VarCopyProc'} error at compilation, you should defined the {\f1\fs20 USEPACKAGES} conditional in your project's options. Open {\f1\fs20 SynCommons.inc} for a description of this conditional, and all over definitions global to all {\i mORMot} units - see @45@.
If you want to compile {\i mORMot} unit into @**packages@, to avoid an obfuscated {\i [DCC Error] @*E2201@ Need imported data reference ($G) to access 'VarCopyProc'} error at compilation, you should defined the {\f1\fs20 USEPACKAGES} conditional in your project's options. Open {\f1\fs20 SynCommons.inc} for a description of this conditional, and all over definitions global to all {\i mORMot} units - see @45@. To avoid related {\i @*E1025@ Unsupported language feature: 'Object'} compilation error, you should probably also set "{\i Generate DCUs only}" in project's options "{\i C/C++ output file generator}".
The framework source code implementation and design tried to be as cross-platform and cross-compiler as possible, since the beginning. It is a lot of work to maintain compatibility towards so many tools and platforms, but we think it is always worth it - especially if you try not depend on {\i Delphi} only, which as shown some backward compatibility issues during its lifetime.
For HTML5 and Mobile clients, our main platform is {\i Smart Mobile Studio}, which is a great combination of ease of use, a powerful {\i SmartPascal} dialect, small applications (much smaller than FMX), with potential packaging as native iOS or {\i Android} applications (via {\i @*PhoneGap@}).
The latest versions of the {\i FreePascal Compiler} together with its great {\i Lazarus} IDE, are now very stable and easy to work with. We don't support {\i CodeTyphon}, since we found some licensing issue with some part of it (e.g. {\i Orca} GUI library origin is doubtful). So we recommend using {\i @*fpcupdeluxe@} - see @203@ - which is maintained by Alfred, a {\i mORMot} contributor. This is amazing to build the whole set of compilers and IDE, with a lot of components, for several platforms (this is a cross-platform project), just from the sources. I like {\i Lazarus} stability and speed much more than {\i Delphi} (did you ever tried to browse and debug {\i included} {\f1\fs20 $I ...} files in the {\i Delphi} IDE? with Lazarus, it is painless), even if the compiler is slower than {\i Delphi}'s, and if the debugger is less integrated and even more unstable than {\i Delphi}'s under Windows (yes, it is possible!). At least, it works, and the {\i Lazarus} IDE is small and efficient. Official {\i @*Linux@} support is available for {\i mORMot} servers, with full features in the {\i FPC} 3.2 branch - we use it on producing on {\i Linux} 64-bit since years.
The latest versions of the {\i FreePascal Compiler} together with its great {\i Lazarus} IDE, are now very stable and easy to work with. We don't support {\i CodeTyphon}, since we found some licensing issue with some part of it (e.g. {\i Orca} GUI library origin is doubtful). So we recommend using {\i @*fpcupdeluxe@} - see @203@ - which is maintained by Alfred, a {\i mORMot} contributor. This is amazing to build the whole set of compilers and IDE, with a lot of components, for several platforms (this is a cross-platform project), just from the sources. I like {\i Lazarus} stability and speed much more than {\i Delphi} (did you ever tried to browse and debug {\i included} {\f1\fs20 $I ...} files in the {\i Delphi} IDE? with Lazarus, it is painless), even if the compiler is slower than {\i Delphi}'s, and if the debugger is less integrated and even more unstable than {\i Delphi}'s under Windows (yes, it is possible!). At least, it works, and the {\i Lazarus} IDE is small and efficient. Official {\i @*Linux@} support is available for {\i mORMot} servers, with full features in the {\i FPC} 3.2 branch - we use it on production with {\i Linux} 64-bit since years.
: SQLite3 static linking for Delphi and FPC
{\i Preliminary note}: if you retrieved the source code from @https://github.com/synopse/mORMot you will have all the needed {\f1\fs20 .obj/.o} static files available in the expected folders. Just ignore this chapter.
In order to maintain our @https://synopse.info/fossil/timeline source code repository in a decent size, we excluded the {\f1\fs20 sqlite3.obj/.o} storage in it, but provide the full source code of the {\i @*SQlite3@} engine in a custom {\f1\fs20 sqlite3.c} file, ready to be compiled with all conditional defined as expected by {\f1\fs20 SynSQlite3Static.pas}. You need to add the official {\i SQlite3} amalgamation file from @https://www.sqlite.org/download.html and put its content into a {\f1\fs20 SQLite3\\amalgamation} sub-folder, for proper compilation. Our custom {\f1\fs20 sqlite3.c} file will add encryption feature to the engine. Also look into {\f1\fs20 SynSQlite3Static.pas} comments if there is any manual patch needed for proper compilation of the amalgamation sourece.
@ -16374,7 +16388,7 @@ See @86@ for more information.
\page
:113 Delphi Installation
{\i Note: for FPC setup, see @125@.}
To setup mORMot for {\i Delphi 6} up to {\i Delphi 10.3 Rio}, you have two ways: either download the framework from archives, or clone our {\i GitHub} repository at @https://github.com/synopse/mORMot
To setup mORMot for {\i Delphi 6} up to the latest {\i Delphi} version, you have two ways: either download the framework from archives, or clone our {\i GitHub} repository at @https://github.com/synopse/mORMot
: Manual download
Download and uncompress the framework archives, including all sub-folders, into a local directory of your computer (for instance, {\f1\fs20 D:\\Dev\\mORMot}).
|%70
@ -16429,11 +16443,12 @@ But since the FPC trunk may be unstable, we will propose to put in place a stabl
For this task, don't download an existing binary release of FPC / Lazarus, but use the {\i @**fpcupdeluxe@} tool, as published at @http://wiki.freepascal.org/fpcupdeluxe - it will allow to build your environment directly from the sources, and install it in a dedicated folder. Several FPC / Lazarus installations, with dedicated revision numbers, may coexist on the same computer: just ensure you run Lazarus from the shortcut created by {\i fpcupdeluxe}.
- Download the latest release of the tool from @https://github.com/LongDirtyAnimAlf/fpcupdeluxe/releases
- Unpack it in a dedicated folder, and run its executable.
- On the main screen, locate on the left the two versions listboxes. Select "fixes" for both {\i FPC version} and {\i Lazarus version}.
- On the main screen, locate on the left the two versions listboxes. Select "{\f1\fs20 3.2}" for {\i FPC version} and "{\f1\fs20 2.1.0}" for {\i Lazarus version}.
- Important note: if you want to cross-compile from Windows to other systems, e.g. install a Linux cross-compiler on Windows, ensure you installed the {\i Win32} FPC compiler and Lazarus, {\i not the Win64} version, which is known to have troubles with {\f1\fs20 currency} support;
- Then build the FPC and Lazarus binaries directly from the latest sources, by clicking on "Install/update FPC+Laz".
Those "fixes" branches are currently used for building our production projects, so are expected to be properly tested and supported. \line At the time of the writing of this documentation, our Lazarus IDE (on Linux) reports using:
- FPC fixes SVN 45428 (3.2.0)
- Lazarus fixes SVN 63179 (2.0.9).
Those branches are currently used for building our production projects, so are expected to be properly tested and supported. \line At the time of the writing of this documentation, our Lazarus IDE (on Linux) reports using:
- FPC SVN 45643 (3.2.0)
- Lazarus SVN 64940 (2.1.0).
One big advantage of {\i fpcupdeluxe} is that you can very easily install cross-compilers for the CPU / OS combinations enumerated at @202@.\line Just go to the "Cross" tab, then select the target systems, and click on "Install compiler".\line It may be needed to download the cross-compiler binaries (once): just select "Yes" when prompted.
You could install {\i mORMot} using {\i fpcupdeluxe}, but we recommend you clone our @https://github.com/synopse/mORMot repository, and setup the expected project paths, as detailed above at @113@.
If you don't want to define a given version, the current {\i trunk} should/could work, if it didn't include any regression at the time you get it - this is why we provide "supported" branches.\line If you want to use the {\i FPC trunk}, please modify line #262 in {\f1\fs20 Synopse.inc} to enable the {\f1\fs20 FPC_PROVIDE_ATTR_TABLE} conditional and support the latest trunk RTTI changes:
@ -16839,7 +16854,7 @@ The {\i Office UI licensing program} was designed by {\i Microsoft} for software
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 {\i Delphi} 6 up to {\i Delphi 10.3 Rio}:
And here is the same application compiled using only VCL components, available from {\i Delphi} 6 up to the latest {\i Delphi} version:
%synfilevcl.png
We did not use yet the Ribbon component as was introduced in {\i 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 {\i 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
@ -18465,7 +18480,7 @@ But please do not forget to put somewhere in your credit window or documentation
For instance, if you select the MPL license, here are the requirements:
- You accept the license terms with no restriction - see @http://www.mozilla.org/MPL/2.0/FAQ.html for additional information;
- You have to publish any modified unit (e.g. {\f1\fs20 SynTaskDialog.pas}) in a public web site (e.g. {\f1\fs20 http://SoftwareCompany.com/MPL}), with a description of applied modifications, and no removal of the original license header in source code;
- You make appear some notice available in the program (About box, documentation, online help), stating e.g.\line {\i This software uses some third-party code of the Synopse mORMot framework (C) 2020 Arnaud Bouchez - {\f1\fs20 https://synopse.info} - under Mozilla Public License 1.1; modified source code is available at {\f1\fs20 http://SoftwareCompany.com/MPL}.}
- You make appear some notice available in the program (About box, documentation, online help), stating e.g.\line {\i This software uses some third-party code of the Synopse mORMot framework (C) 2022 Arnaud Bouchez - {\f1\fs20 https://synopse.info} - under Mozilla Public License 1.1; modified source code is available at {\f1\fs20 http://SoftwareCompany.com/MPL}.}
: Derivate Open Source works
If you want to include part of the framework source code in your own open-source project, you may publish it with a comment similar to this one (as included in the great {\i DelphiWebScript} project by Eric Grange - @http://code.google.com/p/dwscript ):
${
@ -18478,7 +18493,7 @@ $
$ Sample based on official mORMot's sample
$ "SQLite3\Samples\09 - HttpApi web server\HttpApiServer.dpr"
$
$ Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
$ Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
$ Synopse Informatique - https://synopse.info
$
$ Original tri-license: MPL 1.1/GPL 2.0/LGPL 2.1

View File

@ -14,51 +14,42 @@ object MainLogView: TMainLogView
OnCreate = FormCreate
OnKeyDown = FormKeyDown
OnShow = FormShow
LCLVersion = '2.0.8.0'
object Splitter2: TSplitter
Cursor = crVSplit
Left = 0
Height = 4
Top = 635
Width = 860
Align = alBottom
ResizeAnchor = akBottom
end
LCLVersion = '2.0.11.0'
object Splitter3: TSplitter
Left = 837
Height = 635
Height = 583
Top = 0
Width = 4
Visible = False
end
object Splitter1: TSplitter
Left = 829
Height = 635
Height = 583
Top = 0
Width = 4
Visible = False
end
object Splitter4: TSplitter
Left = 833
Height = 635
Height = 583
Top = 0
Width = 4
Visible = False
end
object PanelLeft: TPanel
Left = 257
Height = 635
Height = 583
Top = 0
Width = 150
Align = alLeft
ClientHeight = 635
ClientHeight = 583
ClientWidth = 150
Constraints.MinWidth = 150
TabOrder = 0
object ImageLogo: TImage
Left = 8
Height = 32
Top = 591
Top = 539
Width = 137
Anchors = [akLeft, akRight, akBottom]
Center = True
@ -421,9 +412,27 @@ object MainLogView: TMainLogView
TopIndex = -1
end
end
object Splitter2: TSplitter
Cursor = crVSplit
Left = 0
Height = 4
Top = 583
Width = 860
Align = alBottom
ResizeAnchor = akBottom
end
object PanelBottom: TPanel
Left = 0
Height = 52
Top = 587
Width = 860
Align = alBottom
TabOrder = 9
OnResize = PanelBottomResize
end
object List: TDrawGrid
Left = 841
Height = 635
Height = 583
Top = 0
Width = 19
Align = alClient
@ -447,7 +456,7 @@ object MainLogView: TMainLogView
end
object ProfileList: TDrawGrid
Left = 407
Height = 635
Height = 583
Top = 0
Width = 274
Align = alLeft
@ -468,17 +477,17 @@ object MainLogView: TMainLogView
end
object PanelThread: TPanel
Left = 681
Height = 635
Height = 583
Top = 0
Width = 148
Align = alLeft
ClientHeight = 635
ClientHeight = 583
ClientWidth = 148
TabOrder = 3
Visible = False
object ThreadListBox: TCheckListBox
Left = 1
Height = 593
Height = 541
Top = 1
Width = 146
Align = alClient
@ -492,7 +501,7 @@ object MainLogView: TMainLogView
object pnlThreadBottom: TPanel
Left = 1
Height = 40
Top = 594
Top = 542
Width = 146
Align = alBottom
ClientHeight = 40
@ -512,11 +521,11 @@ object MainLogView: TMainLogView
end
object PanelBrowse: TPanel
Left = 0
Height = 635
Height = 583
Top = 0
Width = 257
Align = alLeft
ClientHeight = 635
ClientHeight = 583
ClientWidth = 257
Constraints.MinWidth = 80
TabOrder = 4

View File

@ -57,6 +57,7 @@ type
fInts: TIntegerDynArray;
fCreateTime: TCreateTime;
fData: TSQLRawBlob;
fFP: double;
published
property Name: RawUTF8 read fName write fName stored AS_UNIQUE;
property Age: integer read fAge write fAge;
@ -65,6 +66,7 @@ type
property Ints: TIntegerDynArray index 1 read fInts write fInts;
property Data: TSQLRawBlob read fData write fData;
property CreateTime: TCreateTime read fCreateTime write fCreateTime;
property FP: double read fFP write fFP;
end;
TTestORM = class(TSynTestCase)
@ -183,8 +185,9 @@ begin
Check(serverTime<>0);
CheckSame(Now,serverTime,0.5);
if System.Pos('MongoDB',Owner.CustomVersions)=0 then
Owner.CustomVersions := Owner.CustomVersions+'Using '+
string(fClient.ServerBuildInfoText);
Owner.CustomVersions := format('%sUsing %s'#13#10'Running on %s'#13#10+
'Compiled with mORMot '+SYNOPSE_FRAMEWORK_VERSION,
[Owner.CustomVersions,fClient.ServerBuildInfoText,OSVersionText]);
fExpectedCount := COLL_COUNT;
end;
@ -452,6 +455,7 @@ begin
R.Value := _ObjFast(['num',i]);
R.Ints := nil;
R.DynArray(1).Add(i);
R.FP := i*7.3445;
Check(fClient.BatchAdd(R,True)>=0);
end;
finally
@ -473,6 +477,7 @@ begin
Check(Length(R.Ints)=1);
Check(R.Ints[0]=aID);
Check(R.CreateTime>=fStartTimeStamp);
CheckSame(R.FP,aID*7.3445);
end;
procedure TTestORM.Retrieve;

View File

@ -19,12 +19,14 @@ type
fTitle: RawUTF8;
fLanguage: RawUTF8;
fAbout: RawUTF8;
fLink: RawUTF8;
published
property Title: RawUTF8 index 80 read fTitle write fTitle;
property Language: RawUTF8 index 3 read fLanguage write fLanguage;
property Description: RawUTF8 index 120 read fDescription write fDescription;
property Copyright: RawUTF8 index 80 read fCopyright write fCopyright;
property About: RawUTF8 read fAbout write fAbout;
property Link: RawUTF8 index 60 read fLink write fLink;
end;
TSQLRecordTimeStamped = class(TSQLRecord)
@ -46,6 +48,7 @@ type
fHashedPassword: RawUTF8;
fLogonName: RawUTF8;
public
function ComputeHash(const PlainPassword: RawUTF8): RawUTF8; virtual;
procedure SetPlainPassword(const PlainPassword: RawUTF8);
function CheckPlainPassword(const PlainPassword: RawUTF8): boolean;
function Name: RawUTF8;
@ -96,6 +99,7 @@ type
OrderID: TIntegerDynArray;
procedure Init(aRest: TSQLRest);
function Get(tagID: integer): RawUTF8;
function GetIDFromIdent(const Ident: RawUTF8): integer;
procedure SaveOccurence(aRest: TSQLRest);
procedure SortTagsByIdent(var Tags: TIntegerDynArray);
function GetAsDocVariantArray: Variant;
@ -106,6 +110,7 @@ type
fAbstract: RawUTF8;
fPublishedMonth: Integer;
fTags: TIntegerDynArray;
fLegacyHash: Int64;
public
class function CurrentPublishedMonth: Integer;
class procedure InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8;
@ -118,6 +123,8 @@ type
property Abstract: RawUTF8 read fAbstract write fAbstract;
// "index 1" below to allow writing e.g. aArticle.DynArray(1).Delete(aIndex)
property Tags: TIntegerDynArray index 1 read fTags write fTags;
// xxhash32 of legacy post_url
property LegacyHash: Int64 read fLegacyHash write fLegacyHash;
end;
TSQLArticleSearch = class(TSQLRecordFTS4Porter)
@ -157,6 +164,8 @@ procedure DotClearFlatImport(Rest: TSQLRest; const aFlatFile: RawUTF8;
var aTagsLookup: TSQLTags; const aDotClearRoot: RawUTF8;
const aStaticFolder: TFileName);
function ComputeLegacyHash(url: PUTF8Char): cardinal;
implementation
@ -176,12 +185,16 @@ end;
{ TSQLSomeone }
const
SALT = 'mORMot';
function TSQLSomeone.ComputeHash(const PlainPassword: RawUTF8): RawUTF8;
var dig: THash256;
begin
PBKDF2_SHA3(SHA3_224,PlainPassword,LogonName+'@mORMot',30,@dig);
BinToHexLower(@dig,28,result);
end;
function TSQLSomeone.CheckPlainPassword(const PlainPassword: RawUTF8): boolean;
begin
result := fHashedPassword=SHA256(SALT+LogonName+PlainPassword);
result := fHashedPassword=ComputeHash(PlainPassword);
end;
function TSQLSomeone.Name: RawUTF8;
@ -191,7 +204,7 @@ end;
procedure TSQLSomeone.SetPlainPassword(const PlainPassword: RawUTF8);
begin
fHashedPassword := SHA256(SALT+LogonName+PlainPassword);
fHashedPassword := ComputeHash(PlainPassword);
end;
@ -233,6 +246,8 @@ begin
inherited;
if (FieldName='') or (FieldName='PublishedMonth') then
Server.CreateSQLIndex(TSQLArticle,'PublishedMonth',false);
if (FieldName='') or (FieldName='LegacyHash') then
Server.CreateSQLIndex(TSQLArticle,'LegacyHash',false);
end;
procedure TSQLArticle.SetPublishedMonth(FromTime: TTimeLog);
@ -260,8 +275,20 @@ begin
result := '';
end;
function TSQLTags.GetIDFromIdent(const Ident: RawUTF8): integer;
var i: PtrInt;
begin
if Ident<>'' then
for i := 0 to length(Lookup)-1 do
if IdemPropNameU(Lookup[i].Ident,Ident) then begin
result := i+1;
exit;
end;
result := 0;
end;
function TSQLTags.GetAsDocVariantArray: Variant;
var i,ndx: Integer;
var i,ndx: PtrInt;
begin
TDocVariant.NewFast(result);
with Lock.ProtectMethod do
@ -438,21 +465,46 @@ begin
until P=nil;
end;
function HttpGet(const aURI: SockString; outHeaders: PSockString=nil;
forceNotSocket: boolean=false; outStatus: PInteger=nil): SockString;
begin
result := '';
if outStatus<>nil then
outStatus^ := 404;
end;
function ComputeLegacyHash(url: PUTF8Char): cardinal;
var c: ansichar;
begin
result := 0;
if url<>nil then
repeat
case url^ of
#0: exit;
'a'..'z', 'A'..'Z', '0'..'9': begin
c := upcase(url^);
result := crc32c(result, @c, 1);
end;
end;
inc(url);
until false;
end;
procedure DotClearFlatImport(Rest: TSQLRest; const aFlatFile: RawUTF8;
var aTagsLookup: TSQLTags; const aDotClearRoot: RawUTF8;
const aStaticFolder: TFileName);
var T,tagTable,postTable: TDotClearTable;
data,urls: TRawUTF8List;
data: TRawUTF8List;
urls: TIntegerDynArray;
info: TSQLBlogInfo;
article: TSQLArticle;
comment: TSQLComment;
tag: TSQLTag;
tags: TRawUTF8DynArray;
tags, notfound: TRawUTF8DynArray;
tagID: TIDDynArray;
tagsCount: integer;
batch: TSQLRestBatch;
PublicFolder: TFileName;
notfound: TRawUTF8DynArray;
r,ndx,post_url,meta_id,meta_type,tag_post_id,postID,post_id: integer;
function FixLinks(P: PUTF8Char): RawUTF8;
@ -501,13 +553,20 @@ var T,tagTable,postTable: TDotClearTable;
continue;
AddNoJSONEscape(B,H-B);
P := H;
if IdemPChar(P,'HTTP://BLOG.SYNOPSE.INFO/') then
inc(P,24)
else if IdemPChar(P,'HTTPS://BLOG.SYNOPSE.INFO/') then
inc(P,25);
if IdemPChar(P,'HTTP://SYNOPSE.INFO') then begin
AddShort('https://synopse.info');
inc(P,19);
end else if P^='/' then begin
if P[1]='?' then
inc(P);
if IdemPChar(P+1,'POST/') then begin
GetUrl(P+6);
i := urls.IndexOf(urlnoparam);
i := IntegerScanIndex(pointer(urls),length(urls),
ComputeLegacyHash(pointer(urlnoparam)));
if i>=0 then begin
AddShort('articleView?id=');
Add(i+1);
@ -582,7 +641,6 @@ begin
end;
auto1 := TAutoFree.Several([
@data,TDotClearTable.Parse(aFlatFile),
@urls,TRawUTF8ListHashed.Create,
@batch,TSQLRestBatch.Create(Rest,TSQLTag,5000)]);
auto2 := TSQLRecord.AutoFree([ // avoid several try..finally
@info,TSQLBlogInfo, @article,TSQLArticle, @comment,TSQLComment, @tag,TSQLTag]);
@ -614,7 +672,7 @@ begin
post_url := postTable.FieldIndexExisting('post_url');
if postTable.Step(true) then
repeat
urls.Add(postTable.FieldBuffer(post_url));
AddInteger(urls,ComputeLegacyHash(postTable.FieldBuffer(post_url)));
until not postTable.Step;
article.Author := TSQLAuthor(1);
article.AuthorName := 'synopse';
@ -631,6 +689,7 @@ begin
article.ModifiedAt := Iso8601ToTimeLog(postTable.GetU(r,'post_upddt'));
article.SetPublishedMonth(article.CreatedAt);
postID := postTable.GetAsInteger(r,post_id);
article.LegacyHash := ComputeLegacyHash(postTable.Get(r,post_url));
article.Tags := nil;
if tagTable.Step(true) then
repeat

View File

@ -38,17 +38,25 @@ var aModel: TSQLModel;
aApplication: TBlogApplication;
aHTTPServer: TSQLHttpServer;
begin
//with TSQLLog.Family do Level := LOG_VERBOSE;
with TSQLLog.Family do begin
Level := LOG_VERBOSE;
PerThreadLog := ptIdentifiedInOnFile;
RotateFileCount := 10;
RotateFileSizeKB := 20 shl 10;
FileExistsAction := acAppend; // as expected by rotation
end;
aModel := CreateModel;
try
aServer := TSQLRestServerDB.Create(aModel,ChangeFileExt(ExeVersion.ProgramFileName,'.db'));
try
aServer.DB.Synchronous := smNormal;
aServer.DB.LockingMode := lmExclusive;
aServer.Options := aServer.Options+[rsoNoTableURI];
aServer.CreateMissingTables;
aApplication := TBlogApplication.Create;
try
aApplication.Start(aServer);
aServer.ServiceMethodRegisterPublishedMethods('', aApplication);
aHTTPServer := TSQLHttpServer.Create('8092',aServer
{$ifndef ONLYUSEHTTPSOCKET},'+',useHttpApiRegisteringURI{$endif});
try

View File

@ -12,6 +12,7 @@ uses
SynCommons,
SynLog,
SynTests,
SynCrtSock,
mORMot,
mORMotMVC,
MVCModel;
@ -31,8 +32,9 @@ type
out Comments: TObjectList);
procedure AuthorView(
var ID: TID; out Author: TSQLAuthor; out Articles: variant);
function Login(
const LogonName,PlainPassword: RawUTF8): TMVCAction;
procedure LoginView;
function Login(const LogonName,PlainPassword,
NewPlainPassword1,NewPlainPassword2: RawUTF8): TMVCAction;
function Logout: TMVCAction;
function ArticleComment(ID: TID; const Title,Comment: RawUTF8): TMVCAction;
function ArticleMatch(const Match: RawUTF8): TMVCAction;
@ -71,7 +73,13 @@ type
procedure TagToText(const Value: variant; out result: variant);
public
procedure Start(aServer: TSQLRestServer); reintroduce;
published
// low-level blog/post blog/tag blog/rss endpoints
procedure Post(Ctxt: TSQLRestServerURIContext);
procedure Tag(Ctxt: TSQLRestServerURIContext);
procedure Rss(Ctxt: TSQLRestServerURIContext);
public
// IBlogApplication implemented methods
procedure Default(var Scope: variant);
procedure ArticleView(ID: TID;
var WithComments: boolean; Direction: integer; var Scope: variant;
@ -79,7 +87,9 @@ type
out Comments: TObjectList);
procedure AuthorView(
var ID: TID; out Author: TSQLAuthor; out Articles: variant);
function Login(const LogonName,PlainPassword: RawUTF8): TMVCAction;
procedure LoginView;
function Login(const LogonName,PlainPassword,
NewPlainPassword1,NewPlainPassword2: RawUTF8): TMVCAction;
function Logout: TMVCAction;
function ArticleComment(ID: TID; const Title,Comment: RawUTF8): TMVCAction;
function ArticleMatch(const Match: RawUTF8): TMVCAction;
@ -130,6 +140,10 @@ begin
finally
Free;
end;
_Safe(fBlogMainInfo)^.AddValue('engine',RawUTF8ToVariant(
'Website powered by mORMot MVC '+SYNOPSE_FRAMEWORK_VERSION+
', compiled with '+GetDelphiCompilerVersion+
', running on '+RawUTF8(ToText(OSVersion32))+'.'));
end;
procedure TBlogApplication.MonthToText(const Value: variant;
@ -172,29 +186,28 @@ begin
auto := TSQLRecord.AutoFree([ // avoid several try..finally
@info,TSQLBlogInfo, @article,TSQLArticle, @comment,TSQLComment, @tag,TSQLTag]);
if not RestModel.Retrieve('',info) then begin // retrieve first item
tmp := StringFromFile('/home/ab/Downloads/2020-06-16-a8003957c2ae6bde5be6ea279c9c9ce4-backup.txt');
tmp := StringFromFile(ExeVersion.ProgramFilePath+'2021-01-20-16-37-default-backup.txt');
info.Language := 'en';
if tmp<>'' then begin
info.Title := 'Synopse Blog';
info.Description := 'Articles, announcements, news, updates and more '+
'about our Open Source projects';
'about Synopse Open Source projects';
info.About := 'Latest information about Synopse Open Source librairies, '+
'mainly the mORMot ORM/SOA/MVC framework, and SynPDF.';
info.Link := 'https://blog.synopse.info';
end else begin
info.Title := 'mORMot BLOG';
info.Description := 'Sample Blog Web Application using Synopse mORMot MVC';
info.About := TSynTestCase.RandomTextParagraph(10,'!');
info.Link := 'http://localhost:8092';
end;
info.About := info.About+#13#10'Website powered by mORMot MVC '+
SYNOPSE_FRAMEWORK_VERSION+', compiled with '+GetDelphiCompilerVersion+
', running on '+ToText(OSVersion32)+'.';
info.Copyright := '&copy;'+ToUTF8(CurrentYear)+'<a href=https://synopse.info>Synopse Informatique</a>';
RestModel.Add(info,true);
end;
if RestModel.TableHasRows(TSQLArticle) then
exit;
if tmp<>'' then begin
DotClearFlatImport(RestModel,tmp,fTagsLookup,'http://blog.synopse.info',
DotClearFlatImport(RestModel,tmp,fTagsLookup,'https://blog.synopse.info',
(TMVCRunOnRestServer(fMainRunner).Views as TMVCViewsMustache).ViewStaticFolder);
exit;
end;
@ -271,20 +284,24 @@ begin
fDefaultData.AddNewProp('tags',fTagsLookup.GetAsDocVariantArray,info);
end;
procedure TBlogApplication.FlushAnyCache;
begin
inherited FlushAnyCache; // call fMainRunner.NotifyContentChanged
fDefaultData.Clear;
end;
{ TBlogApplication - Commands }
const
ARTICLE_FIELDS = 'RowID,Title,Tags,Abstract,ContentHtml,Author,AuthorName,CreatedAt';
ARTICLE_DEFAULT_LIMIT = ' limit 20';
ARTICLE_DEFAULT_ORDER: RawUTF8 = 'order by RowID desc'+ARTICLE_DEFAULT_LIMIT;
procedure TBlogApplication.FlushAnyCache;
begin
inherited FlushAnyCache; // call fMainRunner.NotifyContentChanged
fDefaultData.Clear;
// get last 20 articles
fDefaultData.SetValue('Articles',
RestModel.RetrieveDocVariantArray(TSQLArticle,'',
ARTICLE_DEFAULT_ORDER,[],ARTICLE_FIELDS,nil,@fDefaultLastID));
end;
{ TBlogApplication - Commands }
procedure TBlogApplication.Default(var Scope: variant);
var scop: PDocVariantData;
lastID: TID;
@ -320,14 +337,12 @@ begin
whereClause := whereClause+' and IntegerDynArrayContains(Tags,?)';
end;
SetVariantNull(Scope);
if (lastID=0) and (tag=0) then begin // use simple cache if no parameters
if not fDefaultData.AddExistingProp('Articles',Scope) then begin
articles := RestModel.RetrieveDocVariantArray(TSQLArticle,'',
ARTICLE_DEFAULT_ORDER,[],ARTICLE_FIELDS,nil,@fDefaultLastID);
fDefaultData.AddNewProp('Articles',articles,Scope);
end;
if (lastID=0) and (tag=0) then begin
// use simple cache if no parameters
fDefaultData.AddExistingProp('Articles',Scope); // set by FlushAnyCache
lastID := fDefaultLastID;
end else begin // use more complex request using lastID + tag parameters
end else begin
// use more complex request using lastID + tag parameters
articles := RestModel.RetrieveDocVariantArray(TSQLArticle,'',
whereClause+ARTICLE_DEFAULT_ORDER,[lastID,tag],ARTICLE_FIELDS,nil,@lastID);
scope := _ObjFast(['Articles',articles]);
@ -370,12 +385,31 @@ begin
raise EMVCApplication.CreateGotoError(HTTP_NOTFOUND);
end;
function TBlogApplication.Login(const LogonName, PlainPassword: RawUTF8): TMVCAction;
procedure TBlogApplication.LoginView;
begin
end;
function TBlogApplication.Login(const LogonName, PlainPassword,
NewPlainPassword1, NewPlainPassword2: RawUTF8): TMVCAction;
var Author: TSQLAuthor;
SessionInfo: TCookieData;
newpwd: RawUTF8;
begin
if CurrentSession.CheckAndRetrieve<>0 then begin
GotoError(result,HTTP_BADREQUEST);
if LogonName='' then begin
GotoView(result,'LoginView',[]);
exit;
end;
newpwd := Trim(NewPlainPassword1);
if newpwd<>'' then begin
if (newpwd<>NewPlainPassword2) or
(newpwd=PlainPassword) or
(CurrentSession.CheckAndRetrieve(@SessionInfo,TypeInfo(TCookieData))=0) or
(SessionInfo.AuthorName<>LogonName) then begin
GotoError(result,HTTP_NOTACCEPTABLE);
exit;
end;
end else if CurrentSession.CheckAndRetrieve<>0 then begin
GotoError(result,'Already Logged In',HTTP_BADREQUEST);
exit;
end;
Author := TSQLAuthor.Create(RestModel,'LogonName=?',[LogonName]);
@ -385,6 +419,10 @@ begin
SessionInfo.AuthorID := Author.ID;
SessionInfo.AuthorRights := Author.Rights;
CurrentSession.Initialize(@SessionInfo,TypeInfo(TCookieData));
if newpwd<>'' then begin
Author.SetPlainPassword(newpwd);
RestModel.Update(Author,'HashedPassword');
end;
GotoDefault(result);
end else
GotoError(result,sErrorInvalidLogin);
@ -481,6 +519,83 @@ begin
end;
end;
procedure TBlogApplication.Post(Ctxt: TSQLRestServerURIContext);
var hash, id: Int64;
begin
hash := ComputeLegacyHash(pointer(UrlDecode(Ctxt.URIAfterRoot,5,-1)));
id := RestModel.OneFieldValueInt64(TSQLArticle,'ID',
FormatUTF8('LegacyHash=:(%):', [hash]));
Ctxt.Redirect(FormatUTF8('/%/articleview?id=%',[RestModel.Model.Root,id]));
end;
procedure TBlogApplication.Tag(Ctxt: TSQLRestServerURIContext);
var
id: integer;
begin
id := fTagsLookup.GetIDFromIdent(copy(Ctxt.UriAfterRoot, 5, 100));
Ctxt.Redirect(FormatUTF8('/%/default?scope={tag:%}',[RestModel.Model.Root,id]));
end;
function Esc(const Msg: RawUTF8): RawUTF8;
var i: integer;
ins: RawUTF8;
begin
// fast enough for our purpose to compute some RSS cache
result := Msg;
for i := length(Msg) downto 1 do begin
case Msg[i] of
'"': ins := '&quot';
'&': ins := '&amp';
'<': ins := '&lt';
'>': ins := '&gt';
else Continue;
end;
result[i] := ';';
insert(ins,result,i);
end;
end;
procedure TBlogApplication.Rss(Ctxt: TSQLRestServerURIContext);
function ComputeRss: variant;
var xml, lng, link: RawUTF8;
art: integer;
begin
with _Safe(fBlogMainInfo)^ do
begin
link := U['Link'];
if (link<>'') and (link[length(link)]='/') then
SetLength(link,length(link)-1);
lng := U['Language'];
if lng='' then
lng := 'en_US';
FormatUTF8('<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">'+
'<channel><title>%</title>'+
'<link>%</link><description>%</description>'+
'<lastBuildDate>%</lastBuildDate><language>%</language>',
[Esc(U['Title']),link,Esc(U['Description']),
DateTimeToHTTPDate(NowUTC,'+0000'),lng],xml);
end;
with _Safe(fDefaultData.GetValue('Articles'))^ do
for art := 0 to Count-1 do
with _Safe(Values[art])^ do
xml := FormatUTF8('%'#13'<item><title>%</title>'+
'<link>%/articleview?id=%</link><pubDate>%</pubDate><category>blog</category>'+
'<dc:creator>%</dc:creator><description><![CDATA[%]]></description>'+
'<content:encoded><![CDATA[%]]></content:encoded></item>',
[xml,Esc(U['Title']),link,I['ID'],
DateTimeToHTTPDate(TimeLogToDateTime(I['CreatedAt']),'+0000'),
Esc(U['AuthorName']),U['Abstract'],U['Content']]);
RawUTF8ToVariant(xml+'</channel></rss>',result);
end;
var
rss: variant;
begin
if not fDefaultData.ExistsOrLock('rss',rss) then
fDefaultData.ReplaceAndUnlock('rss',ComputeRss,rss);
Ctxt.Returns(ToUTF8(rss),HTTP_SUCCESS,
HEADER_CONTENT_TYPE+'application/rss+xml; charset=UTF-8',{handle304=}true);
end;
initialization
{$ifndef DELPHI2010}
// manual definition mandatory only if Delphi 2010 RTTI is not available

View File

@ -1,51 +1,51 @@
{{>header}}
{{>masthead}}
<div class="blog-header">
<h1 class="blog-title">{{article.title}}</h1>
<div class="lead blog-description">
Written by <a href="authorView?id={{article.Author}}">{{article.AuthorName}}</a> ({{author.FirstName}} {{author.FamilyName}}) on {{TimeLogToText article.CreatedAt}}<br />
{{#article.tags}}<a href="default?scope={tag:{{.}}}" class="label label-info">{{TagToText .}}</a> {{/article.tags}}
</div>
</div>
<div class="row">
<div class="col-sm-8 blog-main">
{{#article}}
{{#ContentHtml}}{{{abstract}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml abstract}}}{{/ContentHtml}}
<hr>
{{#ContentHtml}}{{{content}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml content}}}{{/ContentHtml}}
{{/article}}
<hr>
<ul class="pager">
<li class="previous"><a href="ArticleView?id={{Article.ID}}&withComments={{withComments}}&direction=1">&larr; Previous</a></li>
<li class="next"><a href="ArticleView?id={{Article.ID}}&withComments={{withComments}}&direction=2">Next &rarr;</a></li>
</ul>
<a name="comments"></a>
{{#WithComments}}
{{#Comments}}
<blockquote>
<p><strong>{{Title}}</strong></p>
<p>{{#ContentHtml}}{{{Content}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml Content}}}{{/ContentHtml}}</p>
<footer>Commented on {{TimeLogToText CreatedAt}} by <a href="authorView?id={{Author}}">{{AuthorName}}</a></<footer>
</blockquote>
<hr>
{{/Comments}}
{{^Comments}}<blockquote>No comment yet.</blockquote>{{/Comments}}
<p><a href="ArticleView?id={{Article.ID}}#comments" class="btn btn-primary btn-sm">Hide Comments</a></p>
{{#main.session.AuthorRights.Comment}}
<a name="addComment"></a>
<form class="form-horizontal" action="ArticleComment#addComment" method="post">
<div class="form-group">
{{#Scope}}<div class="alert alert-danger">{{CommentError}}</div>{{/Scope}}
<input type="hidden" name="id" value={{Article.ID}}>
<input type="text" class="form-control" name="title" placeholder="Title" value="{{Scope.CommentTitle}}">
<textarea class="form-control" name="comment" rows="7" placeholder="Enter a new comment here">{{Scope.CommentContent}}</textarea>
<button type="submit" class="btn btn-primary btn-sm">Add Comment</a>
</div>
</form>
{{/main.session.AuthorRights.Comment}}
</p>
{{/WithComments}}
{{^WithComments}}
<p><a href="ArticleView?id={{Article.ID}}&withComments=true#comments" class="btn btn-primary btn-sm">Show Comments</a></p>
{{/WithComments}}
<div class="blog-header">
<h1 class="blog-title">{{article.title}}</h1>
<div class="lead blog-description">
Written by <a href="authorView?id={{article.Author}}">{{article.AuthorName}}</a> ({{author.FirstName}} {{author.FamilyName}}) on {{TimeLogToText article.CreatedAt}}<br />
{{#article.tags}}<a href="default?scope={tag:{{.}}}" class="label label-info">{{TagToText .}}</a> {{/article.tags}}
</div>
</div>
<div class="row">
<div class="col-sm-8 blog-main">
{{#article}}
{{#ContentHtml}}{{{abstract}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml abstract}}}{{/ContentHtml}}
<hr>
{{#ContentHtml}}{{{content}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml content}}}{{/ContentHtml}}
{{/article}}
<hr>
<ul class="pager">
<li class="previous"><a href="ArticleView?id={{Article.ID}}&withComments={{withComments}}&direction=1">&larr; Previous</a></li>
<li class="next"><a href="ArticleView?id={{Article.ID}}&withComments={{withComments}}&direction=2">Next &rarr;</a></li>
</ul>
<a name="comments"></a>
{{#WithComments}}
{{#Comments}}
<blockquote>
<p><strong>{{Title}}</strong></p>
<p>{{#ContentHtml}}{{{Content}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml Content}}}{{/ContentHtml}}</p>
<footer>Commented on {{TimeLogToText CreatedAt}} by <a href="authorView?id={{Author}}">{{AuthorName}}</a></<footer>
</blockquote>
<hr>
{{/Comments}}
{{^Comments}}<blockquote>No comment yet.</blockquote>{{/Comments}}
<p><a href="ArticleView?id={{Article.ID}}#comments" class="btn btn-primary btn-sm">Hide Comments</a></p>
{{#main.session.AuthorRights.Comment}}
<a name="addComment"></a>
<form class="form-horizontal" action="ArticleComment#addComment" method="post">
<div class="form-group">
{{#Scope}}<div class="alert alert-danger">{{CommentError}}</div>{{/Scope}}
<input type="hidden" name="id" value={{Article.ID}}>
<input type="text" class="form-control" name="title" placeholder="Title" value="{{Scope.CommentTitle}}">
<textarea class="form-control" name="comment" rows="7" placeholder="Enter a new comment here">{{Scope.CommentContent}}</textarea>
<button type="submit" class="btn btn-primary btn-sm">Add Comment</a>
</div>
</form>
{{/main.session.AuthorRights.Comment}}
</p>
{{/WithComments}}
{{^WithComments}}
<p><a href="ArticleView?id={{Article.ID}}&withComments=true#comments" class="btn btn-primary btn-sm">Show Comments</a></p>
{{/WithComments}}
{{>footer}}

View File

@ -1,15 +1,25 @@
{{>header}}
{{>masthead}}
<div class="blog-header">
<h1 class="blog-title">User {{Author.LogonName}}</h1>
<div class="lead blog-description">{{Author.FirstName}} {{Author.FamilyName}}
</div>
</div>
<div class="row">
<div class="col-sm-8 panel">
<div class="panel-heading">Information about <strong>{{Author.LogonName}}</strong></div>
<div class="panel-body">
{{{TSQLAuthor.HtmlTable Author}}}
</div>
<div class="blog-header">
<h1 class="blog-title">User {{Author.LogonName}}</h1>
<div class="lead blog-description">{{Author.FirstName}} {{Author.FamilyName}}
</div>
</div>
<div class="row">
<div class="col-sm-8 panel">
<div class="panel-heading">Information about <strong>{{Author.LogonName}}</strong></div>
<div class="panel-body">
{{{TSQLAuthor.HtmlTable Author}}}
{{#main.session}}
<form class="navbar-form" action="login" method="post">
<span class="navbar-text">Change <strong>{{AuthorName}}</strong> Password:</span>
<input type="text" class="form-control" name="LogonName" placeholder="Username">
<input type="password" class="form-control" name="PlainPassword" placeholder="Old Password">
<input type="password" class="form-control" name="NewPlainPassword1" placeholder="New Password">
<input type="password" class="form-control" name="NewPlainPassword2" placeholder="Repeat New Password">
<button class="btn" type="submit">Change</button>
</form>
{{/main.session}}
</div>
{{>articlerow}}
{{>footer}}

View File

@ -22,8 +22,23 @@
</div><!-- container -->
<div class="blog-footer">
<p>Proudly using the Open Source <a href="http://mormot.net">mORMot ORM/SOA/MVC Framework</a>.<br>
{{{main.blog.engine}}}<br>
<small>{{{main.blog.copyright}}} - page generated in [[GENERATION_TIME_TAG]]</small></p>
<p><a href="#">Back to top</a></p>
<ul class="pager">
<li><a href="#">Back to top</a></li>
<li><a href=rss>RSS feed</a></li>
<li><a href=https://synopse.info/forum/viewtopic.php?id=25>Synopse</a></li>
<li><a href=https://synopse.info/forum>Support Forum</a></li>
<li><a href=https://github.com/synopse>On Github</a></li>
</ul>
{{#main.session}}
<p>
<form class="navbar-form" action="logout">
<span class="navbar-text">Signed in as <a href=AuthorView?id={{AuthorID}}>{{AuthorName}}</a></span>
<button class="btn" type="submit">Logout</button>
</form>
</p>
{{/main.session}}
</div>
</body>
</html>

View File

@ -3,44 +3,23 @@
<div class="blog-masthead">
<div class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbar-main">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="navbar-left">
<a class="navbar-brand" href="default">{{main.blog.title}}</a>
</div>
<div class="navbar-form navbar-right">
<form action="articleMatch" method="post">
<div class="form-group">
<input type="text" class="form-control" name="match" placeholder="Search Expression">
</div>
</form>
</div>
</div>
<center>
<div class="navbar-collapse collapse" id="navbar-main">
{{#main.session}}
<div class="navbar-right">
<form class="navbar-form" action="logout">
<span class="navbar-text">Signed in as <a href=AuthorView?id={{AuthorID}}>{{AuthorName}}</a></span>
<button class="btn" type="submit">Logout</button>
</form>
</div>
{{/main.session}}
{{^main.session}}
<form class="navbar-form navbar-right" action="login" method="post">
<div class="form-group">
<input type="text" class="form-control" name="LogonName" placeholder="Username">
<input type="password" class="form-control" name="PlainPassword" placeholder="Password">
</div>
<button type="submit" class="btn btn-default">Sign In</button>
</form>
{{/main.session}}
</div>
</center>
<div class="navbar-header">
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbar-main">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="navbar-left">
<a class="navbar-brand" href="default">{{main.blog.title}}</a>
</div>
<div class="navbar-form navbar-right">
<form action="articleMatch" method="post">
<div class="form-group">
<input type="text" class="form-control" name="match" placeholder="Search Expression">
</div>
</form>
</div>
</div>
</div>
</div>
</div>

View File

@ -8,6 +8,7 @@ uses
SysUtils,
Classes,
SynCommons,
SynTable,
mORMot,
mORMotHttpClient,
Project31ChatCallbackInterface in 'Project31ChatCallbackInterface.pas';

View File

@ -3,7 +3,7 @@ unit ECCProcess;
(*
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -22,7 +22,7 @@ unit ECCProcess;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -28,7 +28,7 @@ uses
BaseUnix,
{$endif}
{$ifdef LINUXNOTBSD}
SynSystemd,
SynFPCLinux,
mORMotService,
{$endif}
mORMotHttpServer; // HTTP server for RESTful server
@ -163,12 +163,12 @@ const
UNIX_SOCK_PATH = '/tmp/rest-bench.socket';
{$ifdef LINUX}
/// killa process after X second without GEt requests
/// killa process after X second without Get requests
function inactivityWatchdog(p: pointer): ptrint;
var currentRC: TSynMonitorCount64;
begin
repeat
sleep(10000); /// once per 10 second
sleep(10000); // once per 10 second
if aRestServer = nil then // not initialized
continue;
currentRC := aRestServer.Stats.Read;
@ -182,6 +182,7 @@ begin
Result := 0;
end;
{$endif}
begin
// set logging abilities
SQLite3Log.Family.Level := LOG_VERBOSE;
@ -190,10 +191,10 @@ begin
SQLite3Log.Family.NoFile := true; // do not create log files for benchmark
{$ifdef UNIX}
{$ifdef LINUXNOTBSD}
if SynSystemd.ProcessIsStartedBySystemd then begin
if ProcessIsStartedBySystemd then begin
SQLite3Log.Family.EchoToConsole := SQLite3Log.Family.Level;
SQLite3Log.Family.EchoToConsoleUseJournal := true;
if sd.listen_fds(0) = 1 then
if ExternalLibraries.sd_listen_fds(0) = 1 then
url := '' // force to use socket passed by systemd
else
url := '8888';

View File

@ -43,6 +43,15 @@
<OtherUnitFiles Value="../..;../../.."/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<CodeGeneration>
<SmartLinkUnit Value="True"/>
</CodeGeneration>
<Linking>
<Debugging>
<UseExternalDbgSyms Value="True"/>
</Debugging>
<LinkSmart Value="True"/>
</Linking>
</CompilerOptions>
<Debugging>
<Exceptions Count="1">

View File

@ -7,7 +7,7 @@ unit SynJSONTreeView;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -26,7 +26,7 @@ unit SynJSONTreeView;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit SynRestMidasVCL;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynRestMidasVCL;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit SynRestVCL;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynRestVCL;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -7,7 +7,7 @@ unit mORMotRESTFPCInterfaces;
This unit has been generated by a mORMot 1.18.2797 server.
Any manual modification of this file may be lost after regeneration.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
This unit is released under a MPL/GPL/LGPL tri-license,

View File

@ -1,10 +1,57 @@
{$IF (CompilerVersion <= 25)}
type
//other name of constants in XE4
TStyledSettingHelper = record helper for TStyledSetting
const Family = TStyledSetting.ssFamily;
const Size = TStyledSetting.ssSize;
const Style = TStyledSetting.ssStyle;
const FontColor = TStyledSetting.ssFontColor;
const Other = TStyledSetting.ssOther;
end;
TTextAlignHelper = record helper for TTextAlign
const Center = TTextAlign.taCenter;
const Leading = TTextAlign.taLeading;
const Trailing = TTextAlign.taTrailing;
end;
TFmxFormBorderStyleHelper = record helper for TFmxFormBorderStyle
const None = TFmxFormBorderStyle.bsNone;
const Single = TFmxFormBorderStyle.bsSingle;
const Sizeable = TFmxFormBorderStyle.bsSizeable;
const ToolWindow = TFmxFormBorderStyle.bsToolWindow;
const SizeToolWin = TFmxFormBorderStyle.bsSizeToolWin;
end;
TFormPositionHelper = record helper for TFormPosition
//(poDesigned, poDefault, poDefaultPosOnly, poDefaultSizeOnly,
//poScreenCenter, poDesktopCenter, poMainFormCenter, poOwnerFormCenter);
const OwnerFormCenter = TFormPosition.poOwnerFormCenter;
const ScreenCenter = TFormPosition.poScreenCenter;
end;
TBrushKindHelper = record helper for TBrushKind
//(bkNone, bkSolid, bkGradient, bkBitmap, bkResource);
const None = TBrushKind.bkNone;
end;
TAlignLayoutHelper = record helper for TAlignLayout
//(alNone, alTop, alLeft, alRight, alBottom, alMostTop, alMostBottom, alMostLeft, alMostRight, alClient,
//alContents, alCenter, alVertCenter, alHorzCenter, alHorizontal, alVertical, alScale, alFit, alFitLeft, alFitRight);
const Top = TAlignLayout.alTop;
end;
{$IFEND}
var
_ScreenDPI_X : Single = 0;
function ScalingByScreenDPI_N( F:TForm = NIL ):Single;
var
p : TPointF;
{$IF (CompilerVersion >= 28)}
M : TDeviceDisplayMetrics;
{$IFEND}
i : integer;
h : THandle;
begin
@ -29,6 +76,7 @@ begin
end;
{$ENDIF}
{$IF (CompilerVersion >= 28)} //TDeviceDisplayMetrics is available since XE8
if TPlatformServices.Current.SupportsPlatformService( IFMXDeviceMetricsService ) then
begin
M := (TPlatformServices.Current.GetPlatformService(
@ -38,6 +86,7 @@ begin
{$IFDEF MSWINDOWS}96{$ENDIF}
;
end;
{$IFEND}
end;
function ScalingByScreenDPI( F:TForm = NIL ):TPointF;

View File

@ -6,7 +6,7 @@ unit SynTaskDialog;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynTaskDialog;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -139,8 +139,12 @@ uses
{$endif}
{$IFDEF FMX}
System.UITypes, System.Types, System.UIConsts,
FMX.Menus, FMX.Types, FMX.Layouts, FMX.ComboEdit,
FMX.Graphics, FMX.Forms, FMX.Controls, FMX.StdCtrls, FMX.ExtCtrls,
FMX.Menus, FMX.Types, FMX.Layouts,
{$IF (CompilerVersion >= 26.0)}// Delphi XE5 UP
FMX.ComboEdit,
FMX.Graphics,
{$IFEND}
FMX.Forms, FMX.Controls, FMX.StdCtrls, FMX.ExtCtrls,
FMX.ListBox, FMX.Edit, FMX.Objects, FMX.Platform,
{$IFDEF MSWINDOWS}
FMX.Platform.Win

View File

@ -127,6 +127,7 @@ uses
SynZipFiles in '..\SynZipFiles.pas',
{$endif LVCL}
{$ifdef MSWINDOWS}
Windows,
{$ifndef CPU64}
SynSMAPI in '..\SynSMAPI.pas',
SynSM in '..\SynSM.pas',
@ -164,10 +165,12 @@ uses
// SynDBFirebird in '..\SynDBFirebird.pas',
// SynDBDataSet in '..\SynDBDataSet.pas',
// SynDBFireDAC in '..\SynDBDataSet\SynDBFireDAC.pas',
// SynDBUniDAC in '..\SynDBDataSet\SynDBUniDAC.pas',
{$ifdef USEZEOS}
SynDBZeos in '..\SynDBZeos.pas',
{$endif}
{$ifndef DELPHI5OROLDER}
// SynDBVCL in '..\SynDBVCL.pas',
SynDBRemote in '..\SynDBRemote.pas',
mORMotDB in 'mORMotDB.pas',
mORMotMongoDB in 'mORMotMongoDB.pas',

View File

@ -7,7 +7,7 @@ unit TestSQL3FPCInterfaces;
This unit has been generated by a mORMot 1.18.2718 server.
Any manual modification of this file may be lost after regeneration.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
This unit is released under a MPL/GPL/LGPL tri-license,

View File

@ -0,0 +1,666 @@
/*
** Name: cipher_common.c
** Purpose: Implementation of SQLite codecs
** Author: Ulrich Telle
** Created: 2020-02-02
** Copyright: (c) 2006-2020 Ulrich Telle
** License: MIT
*/
#include "cipher_common.h"
static unsigned char padding[] =
"\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
/* --- Codec Descriptor Table --- */
/*
** Common configuration parameters
**
** - cipher : default cipher type
** - hmac_check : flag whether page hmac should be verified on read
*/
static CipherParams commonParams[] =
{
{ "cipher", CODEC_TYPE, CODEC_TYPE, 1, CODEC_TYPE_MAX },
{ "hmac_check", 1, 1, 0, 1 },
CIPHER_PARAMS_SENTINEL
};
SQLITE_PRIVATE int
sqlite3mcGetCipherParameter(CipherParams* cipherParams, const char* paramName)
{
int value = -1;
for (; strlen(cipherParams->m_name) > 0; ++cipherParams)
{
if (sqlite3_stricmp(paramName, cipherParams->m_name) == 0) break;
}
if (strlen(cipherParams->m_name) > 0)
{
value = cipherParams->m_value;
cipherParams->m_value = cipherParams->m_default;
}
return value;
}
static CodecParameter globalCodecParameterTable[] =
{
{ "global", CODEC_TYPE_UNKNOWN, commonParams },
#if HAVE_CIPHER_AES_128_CBC
{ "aes128cbc", CODEC_TYPE_AES128, mcAES128Params },
#endif
#if HAVE_CIPHER_AES_256_CBC
{ "aes256cbc", CODEC_TYPE_AES256, mcAES256Params },
#endif
#if HAVE_CIPHER_CHACHA20
{ "chacha20", CODEC_TYPE_CHACHA20, mcChaCha20Params },
#endif
#if HAVE_CIPHER_SQLCIPHER
{ "sqlcipher", CODEC_TYPE_SQLCIPHER, mcSQLCipherParams },
#endif
#if HAVE_CIPHER_RC4
{ "rc4", CODEC_TYPE_RC4, mcRC4Params },
#endif
{ "", CODEC_TYPE_UNKNOWN, NULL }
};
#define CODEC_TYPE_UNKNOWN 0
#define CODEC_TYPE_AES128 1
#define CODEC_TYPE_AES256 2
#define CODEC_TYPE_CHACHA20 3
#define CODEC_TYPE_SQLCIPHER 4
#define CODEC_TYPE_RC4 5
SQLITE_PRIVATE CodecParameter*
sqlite3mcCloneCodecParameterTable()
{
/* Count number of codecs and cipher parameters */
int nTables = 0;
int nParams = 0;
int j, k, n;
CipherParams* cloneCipherParams;
CodecParameter* cloneCodecParams;
for (j = 0; strlen(globalCodecParameterTable[j].m_name) > 0; ++j)
{
CipherParams* params = globalCodecParameterTable[j].m_params;
for (k = 0; strlen(params[k].m_name) > 0; ++k);
nParams += k;
}
nTables = j;
/* Allocate memory for cloned codec parameter tables (including sentinel for each table) */
cloneCipherParams = (CipherParams*) sqlite3_malloc((nParams + nTables) * sizeof(CipherParams));
cloneCodecParams = (CodecParameter*) sqlite3_malloc((nTables + 1) * sizeof(CodecParameter));
/* Create copy of tables */
if (cloneCodecParams != NULL)
{
int offset = 0;
for (j = 0; j < nTables; ++j)
{
CipherParams* params = globalCodecParameterTable[j].m_params;
cloneCodecParams[j].m_name = globalCodecParameterTable[j].m_name;
cloneCodecParams[j].m_params = &cloneCipherParams[offset];
for (n = 0; strlen(params[n].m_name) > 0; ++n);
/* Copy all parameters of the current table (including sentinel) */
for (k = 0; k <= n; ++k)
{
cloneCipherParams[offset + k].m_name = params[k].m_name;
cloneCipherParams[offset + k].m_value = params[k].m_value;
cloneCipherParams[offset + k].m_default = params[k].m_default;
cloneCipherParams[offset + k].m_minValue = params[k].m_minValue;
cloneCipherParams[offset + k].m_maxValue = params[k].m_maxValue;
}
offset += (n + 1);
}
cloneCodecParams[nTables].m_name = globalCodecParameterTable[nTables].m_name;
cloneCodecParams[nTables].m_params = NULL;
}
else
{
sqlite3_free(cloneCipherParams);
}
return cloneCodecParams;
}
SQLITE_PRIVATE void
sqlite3mcFreeCodecParameterTable(CodecParameter* codecParams)
{
sqlite3_free(codecParams[0].m_params);
sqlite3_free(codecParams);
}
static const CipherDescriptor mcSentinelDescriptor =
{
"", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
static const CipherDescriptor mcDummyDescriptor =
{
"@dummy@", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
static const CipherDescriptor* codecDescriptorTable[] =
{
#if HAVE_CIPHER_AES_128_CBC
&mcAES128Descriptor,
#else
&mcDummyDescriptor,
#endif
#if HAVE_CIPHER_AES_256_CBC
&mcAES256Descriptor,
#else
&mcDummyDescriptor,
#endif
#if HAVE_CIPHER_CHACHA20
&mcChaCha20Descriptor,
#else
&mcDummyDescriptor,
#endif
#if HAVE_CIPHER_SQLCIPHER
&mcSQLCipherDescriptor,
#else
&mcDummyDescriptor,
#endif
#if HAVE_CIPHER_RC4
&mcRC4Descriptor,
#else
&mcDummyDescriptor,
#endif
&mcSentinelDescriptor
};
/* --- Codec --- */
SQLITE_PRIVATE CodecParameter*
sqlite3mcGetCodecParams(sqlite3* db);
SQLITE_PRIVATE int
sqlite3mcGetCipherType(sqlite3* db)
{
CodecParameter* codecParams = (db != NULL) ? sqlite3mcGetCodecParams(db) : globalCodecParameterTable;
CipherParams* cipherParamTable = (codecParams != NULL) ? codecParams[0].m_params : commonParams;
int cipherType = CODEC_TYPE;
CipherParams* cipher = cipherParamTable;
for (; strlen(cipher->m_name) > 0; ++cipher)
{
if (sqlite3_stricmp("cipher", cipher->m_name) == 0) break;
}
if (strlen(cipher->m_name) > 0)
{
cipherType = cipher->m_value;
cipher->m_value = cipher->m_default;
}
return cipherType;
}
SQLITE_PRIVATE CipherParams*
sqlite3mcGetCipherParams(sqlite3* db, int cypherType)
{
CodecParameter* codecParams = (db != NULL) ? sqlite3mcGetCodecParams(db) : globalCodecParameterTable;
CipherParams* cipherParamTable = (codecParams != NULL) ? codecParams[cypherType].m_params : globalCodecParameterTable[cypherType].m_params;
return cipherParamTable;
}
SQLITE_PRIVATE int
sqlite3mcCodecInit(Codec* codec)
{
int rc = SQLITE_OK;
if (codec != NULL)
{
codec->m_isEncrypted = 0;
codec->m_hmacCheck = 1;
codec->m_hasReadCipher = 0;
codec->m_readCipherType = CODEC_TYPE_UNKNOWN;
codec->m_readCipher = NULL;
codec->m_readReserved = -1;
codec->m_hasWriteCipher = 0;
codec->m_writeCipherType = CODEC_TYPE_UNKNOWN;
codec->m_writeCipher = NULL;
codec->m_writeReserved = -1;
codec->m_db = NULL;
codec->m_bt = NULL;
codec->m_btShared = NULL;
memset(codec->m_page, 0, sizeof(codec->m_page));
codec->m_pageSize = 0;
codec->m_reserved = 0;
codec->m_hasKeySalt = 0;
memset(codec->m_keySalt, 0, sizeof(codec->m_keySalt));
}
else
{
rc = SQLITE_NOMEM;
}
return rc;
}
SQLITE_PRIVATE void
sqlite3mcCodecTerm(Codec* codec)
{
if (codec->m_readCipher != NULL)
{
codecDescriptorTable[codec->m_readCipherType - 1]->m_freeCipher(codec->m_readCipher);
codec->m_readCipher = NULL;
}
if (codec->m_writeCipher != NULL)
{
codecDescriptorTable[codec->m_writeCipherType - 1]->m_freeCipher(codec->m_writeCipher);
codec->m_writeCipher = NULL;
}
memset(codec, 0, sizeof(Codec));
}
SQLITE_PRIVATE void
sqlite3mcClearKeySalt(Codec* codec)
{
codec->m_hasKeySalt = 0;
memset(codec->m_keySalt, 0, sizeof(codec->m_keySalt));
}
SQLITE_PRIVATE int
sqlite3mcCodecSetup(Codec* codec, int cipherType, char* userPassword, int passwordLength)
{
int rc = SQLITE_OK;
CipherParams* globalParams = sqlite3mcGetCipherParams(codec->m_db, 0);
codec->m_isEncrypted = 1;
codec->m_hmacCheck = sqlite3mcGetCipherParameter(globalParams, "hmac_check");
codec->m_hasReadCipher = 1;
codec->m_hasWriteCipher = 1;
codec->m_readCipherType = cipherType;
codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType-1]->m_allocateCipher(codec->m_db);
if (codec->m_readCipher != NULL)
{
unsigned char* keySalt = (codec->m_hasKeySalt != 0) ? codec->m_keySalt : NULL;
sqlite3mcGenerateReadKey(codec, userPassword, passwordLength, keySalt);
rc = sqlite3mcCopyCipher(codec, 1);
}
else
{
rc = SQLITE_NOMEM;
}
return rc;
}
SQLITE_PRIVATE int
sqlite3mcSetupWriteCipher(Codec* codec, int cipherType, char* userPassword, int passwordLength)
{
int rc = SQLITE_OK;
CipherParams* globalParams = sqlite3mcGetCipherParams(codec->m_db, 0);
if (codec->m_writeCipher != NULL)
{
codecDescriptorTable[codec->m_writeCipherType-1]->m_freeCipher(codec->m_writeCipher);
}
codec->m_isEncrypted = 1;
codec->m_hmacCheck = sqlite3mcGetCipherParameter(globalParams, "hmac_check");
codec->m_hasWriteCipher = 1;
codec->m_writeCipherType = cipherType;
codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType-1]->m_allocateCipher(codec->m_db);
if (codec->m_writeCipher != NULL)
{
unsigned char* keySalt = (codec->m_hasKeySalt != 0) ? codec->m_keySalt : NULL;
sqlite3mcGenerateWriteKey(codec, userPassword, passwordLength, keySalt);
}
else
{
rc = SQLITE_NOMEM;
}
return rc;
}
SQLITE_PRIVATE void
sqlite3mcSetIsEncrypted(Codec* codec, int isEncrypted)
{
codec->m_isEncrypted = isEncrypted;
}
SQLITE_PRIVATE void
sqlite3mcSetReadCipherType(Codec* codec, int cipherType)
{
codec->m_readCipherType = cipherType;
}
SQLITE_PRIVATE void
sqlite3mcSetWriteCipherType(Codec* codec, int cipherType)
{
codec->m_writeCipherType = cipherType;
}
SQLITE_PRIVATE void
sqlite3mcSetHasReadCipher(Codec* codec, int hasReadCipher)
{
codec->m_hasReadCipher = hasReadCipher;
}
SQLITE_PRIVATE void
sqlite3mcSetHasWriteCipher(Codec* codec, int hasWriteCipher)
{
codec->m_hasWriteCipher = hasWriteCipher;
}
SQLITE_PRIVATE void
sqlite3mcSetDb(Codec* codec, sqlite3* db)
{
codec->m_db = db;
}
SQLITE_PRIVATE void
sqlite3mcSetBtree(Codec* codec, Btree* bt)
{
codec->m_bt = bt;
codec->m_btShared = bt->pBt;
}
SQLITE_PRIVATE void
sqlite3mcSetReadReserved(Codec* codec, int reserved)
{
codec->m_readReserved = reserved;
}
SQLITE_PRIVATE void
sqlite3mcSetWriteReserved(Codec* codec, int reserved)
{
codec->m_writeReserved = reserved;
}
SQLITE_PRIVATE int
sqlite3mcIsEncrypted(Codec* codec)
{
return codec->m_isEncrypted;
}
SQLITE_PRIVATE int
sqlite3mcHasReadCipher(Codec* codec)
{
return codec->m_hasReadCipher;
}
SQLITE_PRIVATE int
sqlite3mcHasWriteCipher(Codec* codec)
{
return codec->m_hasWriteCipher;
}
SQLITE_PRIVATE Btree*
sqlite3mcGetBtree(Codec* codec)
{
return codec->m_bt;
}
SQLITE_PRIVATE BtShared*
sqlite3mcGetBtShared(Codec* codec)
{
return codec->m_btShared;
}
SQLITE_PRIVATE int
sqlite3mcGetPageSize(Codec* codec)
{
return codec->m_btShared->pageSize;
}
SQLITE_PRIVATE int
sqlite3mcGetReadReserved(Codec* codec)
{
return codec->m_readReserved;
}
SQLITE_PRIVATE int
sqlite3mcGetWriteReserved(Codec* codec)
{
return codec->m_writeReserved;
}
SQLITE_PRIVATE unsigned char*
sqlite3mcGetPageBuffer(Codec* codec)
{
return &codec->m_page[4];
}
SQLITE_PRIVATE int
sqlite3mcGetLegacyReadCipher(Codec* codec)
{
int legacy = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType - 1]->m_getLegacy(codec->m_readCipher) : 0;
return legacy;
}
SQLITE_PRIVATE int
sqlite3mcGetLegacyWriteCipher(Codec* codec)
{
int legacy = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getLegacy(codec->m_writeCipher) : -1;
return legacy;
}
SQLITE_PRIVATE int
sqlite3mcGetPageSizeReadCipher(Codec* codec)
{
int pageSize = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType - 1]->m_getPageSize(codec->m_readCipher) : 0;
return pageSize;
}
SQLITE_PRIVATE int
sqlite3mcGetPageSizeWriteCipher(Codec* codec)
{
int pageSize = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getPageSize(codec->m_writeCipher) : -1;
return pageSize;
}
SQLITE_PRIVATE int
sqlite3mcGetReservedReadCipher(Codec* codec)
{
int reserved = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType-1]->m_getReserved(codec->m_readCipher) : -1;
return reserved;
}
SQLITE_PRIVATE int
sqlite3mcGetReservedWriteCipher(Codec* codec)
{
int reserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType-1]->m_getReserved(codec->m_writeCipher) : -1;
return reserved;
}
SQLITE_PRIVATE int
sqlite3mcReservedEqual(Codec* codec)
{
int readReserved = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType-1]->m_getReserved(codec->m_readCipher) : -1;
int writeReserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType-1]->m_getReserved(codec->m_writeCipher) : -1;
return (readReserved == writeReserved);
}
SQLITE_PRIVATE unsigned char*
sqlite3mcGetSaltWriteCipher(Codec* codec)
{
unsigned char* salt = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1]->m_getSalt(codec->m_writeCipher) : NULL;
return salt;
}
SQLITE_PRIVATE int
sqlite3mcCodecCopy(Codec* codec, Codec* other)
{
int rc = SQLITE_OK;
codec->m_isEncrypted = other->m_isEncrypted;
codec->m_hmacCheck = other->m_hmacCheck;
codec->m_hasReadCipher = other->m_hasReadCipher;
codec->m_hasWriteCipher = other->m_hasWriteCipher;
codec->m_readCipherType = other->m_readCipherType;
codec->m_writeCipherType = other->m_writeCipherType;
codec->m_readCipher = NULL;
codec->m_writeCipher = NULL;
codec->m_readReserved = other->m_readReserved;
codec->m_writeReserved = other->m_writeReserved;
if (codec->m_hasReadCipher)
{
codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType - 1]->m_allocateCipher(codec->m_db);
if (codec->m_readCipher != NULL)
{
codecDescriptorTable[codec->m_readCipherType - 1]->m_cloneCipher(codec->m_readCipher, other->m_readCipher);
}
else
{
rc = SQLITE_NOMEM;
}
}
if (codec->m_hasWriteCipher)
{
codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType - 1]->m_allocateCipher(codec->m_db);
if (codec->m_writeCipher != NULL)
{
codecDescriptorTable[codec->m_writeCipherType - 1]->m_cloneCipher(codec->m_writeCipher, other->m_writeCipher);
}
else
{
rc = SQLITE_NOMEM;
}
}
codec->m_db = other->m_db;
codec->m_bt = other->m_bt;
codec->m_btShared = other->m_btShared;
return rc;
}
SQLITE_PRIVATE int
sqlite3mcCodecCompare(Codec* codec1, Codec* codec2)
{
int equal = 0;
if (codec1->m_hasReadCipher == codec2->m_hasReadCipher &&
codec1->m_hasWriteCipher == codec2->m_hasWriteCipher)
{
int eqRead = (codec1->m_hasReadCipher) ? codec1->m_readCipherType == codec2->m_readCipherType : 1;
int eqWrite = (codec1->m_hasWriteCipher) ? codec1->m_writeCipherType == codec2->m_writeCipherType : 1;
if (eqRead && eqWrite)
{
eqRead = (codec1->m_hasReadCipher) ? codecDescriptorTable[codec1->m_readCipherType - 1]->m_compareCipher(codec1->m_readCipher, codec2->m_readCipher) : 1;
eqWrite = (codec1->m_hasWriteCipher) ? codecDescriptorTable[codec1->m_writeCipherType - 1]->m_compareCipher(codec1->m_writeCipher, codec2->m_writeCipher) : 1;
equal = eqRead && eqWrite;
}
}
return equal;
}
SQLITE_PRIVATE int
sqlite3mcCopyCipher(Codec* codec, int read2write)
{
int rc = SQLITE_OK;
if (read2write)
{
if (codec->m_writeCipher != NULL && codec->m_writeCipherType != codec->m_readCipherType)
{
codecDescriptorTable[codec->m_writeCipherType-1]->m_freeCipher(codec->m_writeCipher);
codec->m_writeCipher = NULL;
}
if (codec->m_writeCipher == NULL)
{
codec->m_writeCipherType = codec->m_readCipherType;
codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType-1]->m_allocateCipher(codec->m_db);
}
if (codec->m_writeCipher != NULL)
{
codecDescriptorTable[codec->m_writeCipherType-1]->m_cloneCipher(codec->m_writeCipher, codec->m_readCipher);
}
else
{
rc = SQLITE_NOMEM;
}
}
else
{
if (codec->m_readCipher != NULL && codec->m_readCipherType != codec->m_writeCipherType)
{
codecDescriptorTable[codec->m_readCipherType-1]->m_freeCipher(codec->m_readCipher);
codec->m_readCipher = NULL;
}
if (codec->m_readCipher == NULL)
{
codec->m_readCipherType = codec->m_writeCipherType;
codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType-1]->m_allocateCipher(codec->m_db);
}
if (codec->m_readCipher != NULL)
{
codecDescriptorTable[codec->m_readCipherType-1]->m_cloneCipher(codec->m_readCipher, codec->m_writeCipher);
}
else
{
rc = SQLITE_NOMEM;
}
}
return rc;
}
SQLITE_PRIVATE void
sqlite3mcPadPassword(char* password, int pswdlen, unsigned char pswd[32])
{
int j;
int p = 0;
int m = pswdlen;
if (m > 32) m = 32;
for (j = 0; j < m; j++)
{
pswd[p++] = (unsigned char) password[j];
}
for (j = 0; p < 32 && j < 32; j++)
{
pswd[p++] = padding[j];
}
}
SQLITE_PRIVATE void
sqlite3mcGenerateReadKey(Codec* codec, char* userPassword, int passwordLength, unsigned char* cipherSalt)
{
codecDescriptorTable[codec->m_readCipherType-1]->m_generateKey(codec->m_readCipher, codec->m_btShared, userPassword, passwordLength, 0, cipherSalt);
}
SQLITE_PRIVATE void
sqlite3mcGenerateWriteKey(Codec* codec, char* userPassword, int passwordLength, unsigned char* cipherSalt)
{
codecDescriptorTable[codec->m_writeCipherType-1]->m_generateKey(codec->m_writeCipher, codec->m_btShared, userPassword, passwordLength, 1, cipherSalt);
}
SQLITE_PRIVATE int
sqlite3mcEncrypt(Codec* codec, int page, unsigned char* data, int len, int useWriteKey)
{
int cipherType = (useWriteKey) ? codec->m_writeCipherType : codec->m_readCipherType;
void* cipher = (useWriteKey) ? codec->m_writeCipher : codec->m_readCipher;
int reserved = (useWriteKey) ? (codec->m_writeReserved >= 0) ? codec->m_writeReserved : codec->m_reserved
: (codec->m_readReserved >= 0) ? codec->m_readReserved : codec->m_reserved;
return codecDescriptorTable[cipherType-1]->m_encryptPage(cipher, page, data, len, reserved);
}
SQLITE_PRIVATE int
sqlite3mcDecrypt(Codec* codec, int page, unsigned char* data, int len)
{
int cipherType = codec->m_readCipherType;
void* cipher = codec->m_readCipher;
int reserved = (codec->m_readReserved >= 0) ? codec->m_readReserved : codec->m_reserved;
return codecDescriptorTable[cipherType-1]->m_decryptPage(cipher, page, data, len, reserved, codec->m_hmacCheck);
}
SQLITE_PRIVATE void
sqlite3mcConfigureSQLCipherVersion(sqlite3* db, int configDefault, int legacyVersion)
{
static char* stdNames[] = { "legacy_page_size", "kdf_iter", "hmac_use", "kdf_algorithm", "hmac_algorithm", NULL };
static char* defNames[] = { "default:legacy_page_size", "default:kdf_iter", "default:hmac_use", "default:kdf_algorithm", "default:hmac_algorithm", NULL };
static int versionParams[SQLCIPHER_VERSION_MAX][5] =
{
{ 1024, 4000, 0, SQLCIPHER_KDF_ALGORITHM_SHA1, SQLCIPHER_HMAC_ALGORITHM_SHA1 },
{ 1024, 4000, 1, SQLCIPHER_KDF_ALGORITHM_SHA1, SQLCIPHER_HMAC_ALGORITHM_SHA1 },
{ 1024, 64000, 1, SQLCIPHER_KDF_ALGORITHM_SHA1, SQLCIPHER_HMAC_ALGORITHM_SHA1 },
{ 4096, 256000, 1, SQLCIPHER_KDF_ALGORITHM_SHA512, SQLCIPHER_HMAC_ALGORITHM_SHA512 }
};
if (legacyVersion > 0 && legacyVersion <= SQLCIPHER_VERSION_MAX)
{
char** names = (configDefault != 0) ? defNames : stdNames;
int* values = &versionParams[legacyVersion - 1][0];
int j;
for (j = 0; names[j] != NULL; ++j)
{
sqlite3mc_config_cipher(db, "sqlcipher", names[j], values[j]);
}
}
}

View File

@ -0,0 +1,233 @@
/*
** Name: cipher_common.h
** Purpose: Header for the ciphers of SQLite3 Multiple Ciphers
** Author: Ulrich Telle
** Created: 2020-02-02
** Copyright: (c) 2006-2020 Ulrich Telle
** License: MIT
*/
#ifndef CIPHER_COMMON_H_
#define CIPHER_COMMON_H_
#include "sqlite3mc.h"
/*
// ATTENTION: Macro similar to that in pager.c
// TODO: Check in case of new version of SQLite
*/
#define WX_PAGER_MJ_PGNO(x) ((PENDING_BYTE/(x))+1)
#define CODEC_TYPE_DEFAULT CODEC_TYPE_CHACHA20
#ifndef CODEC_TYPE
#define CODEC_TYPE CODEC_TYPE_DEFAULT
#endif
#if CODEC_TYPE < 1 || CODEC_TYPE > CODEC_TYPE_MAX
#error "Invalid codec type selected"
#endif
#define MAXKEYLENGTH 32
#define KEYLENGTH_AES128 16
#define KEYLENGTH_AES256 32
#define KEYSALT_LENGTH 16
#define CODEC_SHA_ITER 4001
typedef struct _Codec
{
int m_isEncrypted;
int m_hmacCheck;
/* Read cipher */
int m_hasReadCipher;
int m_readCipherType;
void* m_readCipher;
int m_readReserved;
/* Write cipher */
int m_hasWriteCipher;
int m_writeCipherType;
void* m_writeCipher;
int m_writeReserved;
sqlite3* m_db; /* Pointer to DB */
Btree* m_bt; /* Pointer to B-tree used by DB */
BtShared* m_btShared; /* Pointer to shared B-tree used by DB */
unsigned char m_page[SQLITE_MAX_PAGE_SIZE + 24];
int m_pageSize;
int m_reserved;
int m_hasKeySalt;
unsigned char m_keySalt[KEYSALT_LENGTH];
} Codec;
#define CIPHER_PARAMS_SENTINEL { "", 0, 0, 0, 0 }
#define CIPHER_PAGE1_OFFSET 24
typedef struct _CipherParams
{
char* m_name;
int m_value;
int m_default;
int m_minValue;
int m_maxValue;
} CipherParams;
typedef struct _CodecParameter
{
char* m_name;
int m_id;
CipherParams* m_params;
} CodecParameter;
typedef void* (*AllocateCipher_t)(sqlite3* db);
typedef void (*FreeCipher_t)(void* cipher);
typedef void (*CloneCipher_t)(void* cipherTo, void* cipherFrom);
typedef int (*CompareCipher_t)(void* cipher1, void* cipher2);
typedef int (*GetLegacy_t)(void* cipher);
typedef int (*GetPageSize_t)(void* cipher);
typedef int (*GetReserved_t)(void* cipher);
typedef unsigned char* (*GetSalt_t)(void* cipher);
typedef void (*GenerateKey_t)(void* cipher, BtShared* pBt, char* userPassword, int passwordLength, int rekey, unsigned char* cipherSalt);
typedef int (*EncryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved);
typedef int (*DecryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved, int hmacCheck);
typedef struct _CodecDescriptor
{
char m_name[32];
AllocateCipher_t m_allocateCipher;
FreeCipher_t m_freeCipher;
CloneCipher_t m_cloneCipher;
CompareCipher_t m_compareCipher;
GetLegacy_t m_getLegacy;
GetPageSize_t m_getPageSize;
GetReserved_t m_getReserved;
GetSalt_t m_getSalt;
GenerateKey_t m_generateKey;
EncryptPage_t m_encryptPage;
DecryptPage_t m_decryptPage;
} CipherDescriptor;
SQLITE_PRIVATE int sqlite3mcGetCipherParameter(CipherParams* cipherParams, const char* paramName);
SQLITE_PRIVATE int sqlite3mcGetCipherType(sqlite3* db);
SQLITE_PRIVATE CipherParams* sqlite3mcGetCipherParams(sqlite3* db, int cypherType);
SQLITE_PRIVATE int sqlite3mcCodecInit(Codec* codec);
SQLITE_PRIVATE void sqlite3mcCodecTerm(Codec* codec);
SQLITE_PRIVATE void sqlite3mcClearKeySalt(Codec* codec);
SQLITE_PRIVATE int sqlite3mcCodecSetup(Codec* codec, int cipherType, char* userPassword, int passwordLength);
SQLITE_PRIVATE int sqlite3mcSetupWriteCipher(Codec* codec, int cipherType, char* userPassword, int passwordLength);
SQLITE_PRIVATE void sqlite3mcSetIsEncrypted(Codec* codec, int isEncrypted);
SQLITE_PRIVATE void sqlite3mcSetReadCipherType(Codec* codec, int cipherType);
SQLITE_PRIVATE void sqlite3mcSetWriteCipherType(Codec* codec, int cipherType);
SQLITE_PRIVATE void sqlite3mcSetHasReadCipher(Codec* codec, int hasReadCipher);
SQLITE_PRIVATE void sqlite3mcSetHasWriteCipher(Codec* codec, int hasWriteCipher);
SQLITE_PRIVATE void sqlite3mcSetDb(Codec* codec, sqlite3* db);
SQLITE_PRIVATE void sqlite3mcSetBtree(Codec* codec, Btree* bt);
SQLITE_PRIVATE void sqlite3mcSetReadReserved(Codec* codec, int reserved);
SQLITE_PRIVATE void sqlite3mcSetWriteReserved(Codec* codec, int reserved);
SQLITE_PRIVATE int sqlite3mcIsEncrypted(Codec* codec);
SQLITE_PRIVATE int sqlite3mcHasReadCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcHasWriteCipher(Codec* codec);
SQLITE_PRIVATE Btree* sqlite3mcGetBtree(Codec* codec);
SQLITE_PRIVATE BtShared* sqlite3mcGetBtShared(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetPageSize(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetReadReserved(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetWriteReserved(Codec* codec);
SQLITE_PRIVATE unsigned char* sqlite3mcGetPageBuffer(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetLegacyReadCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetLegacyWriteCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetPageSizeReadCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetPageSizeWriteCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetReservedReadCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetReservedWriteCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcReservedEqual(Codec* codec);
SQLITE_PRIVATE unsigned char* sqlite3mcGetSaltWriteCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcCodecCopy(Codec* codec, Codec* other);
SQLITE_PRIVATE int sqlite3mcCodecCompare(Codec* codec1, Codec* codec2);
SQLITE_PRIVATE void sqlite3mcGenerateReadKey(Codec* codec, char* userPassword, int passwordLength, unsigned char* cipherSalt);
SQLITE_PRIVATE void sqlite3mcGenerateWriteKey(Codec* codec, char* userPassword, int passwordLength, unsigned char* cipherSalt);
SQLITE_PRIVATE int sqlite3mcEncrypt(Codec* codec, int page, unsigned char* data, int len, int useWriteKey);
SQLITE_PRIVATE int sqlite3mcDecrypt(Codec* codec, int page, unsigned char* data, int len);
SQLITE_PRIVATE int sqlite3mcCopyCipher(Codec* codec, int read2write);
SQLITE_PRIVATE int sqlite3mcCodecSetup(Codec* codec, int cipherType, char* userPassword, int passwordLength);
SQLITE_PRIVATE int sqlite3mcSetupWriteCipher(Codec* codec, int cipherType, char* userPassword, int passwordLength);
SQLITE_PRIVATE void sqlite3mcSetIsEncrypted(Codec* codec, int isEncrypted);
SQLITE_PRIVATE void sqlite3mcSetReadCipherType(Codec* codec, int cipherType);
SQLITE_PRIVATE void sqlite3mcSetWriteCipherType(Codec* codec, int cipherType);
SQLITE_PRIVATE void sqlite3mcSetHasReadCipher(Codec* codec, int hasReadCipher);
SQLITE_PRIVATE void sqlite3mcSetHasWriteCipher(Codec* codec, int hasWriteCipher);
SQLITE_PRIVATE void sqlite3mcSetDb(Codec* codec, sqlite3* db);
SQLITE_PRIVATE void sqlite3mcSetBtree(Codec* codec, Btree* bt);
SQLITE_PRIVATE void sqlite3mcSetReadReserved(Codec* codec, int reserved);
SQLITE_PRIVATE void sqlite3mcSetWriteReserved(Codec* codec, int reserved);
SQLITE_PRIVATE int sqlite3mcIsEncrypted(Codec* codec);
SQLITE_PRIVATE int sqlite3mcHasReadCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcHasWriteCipher(Codec* codec);
SQLITE_PRIVATE Btree* sqlite3mcGetBtree(Codec* codec);
SQLITE_PRIVATE BtShared* sqlite3mcGetBtShared(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetPageSize(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetReadReserved(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetWriteReserved(Codec* codec);
SQLITE_PRIVATE unsigned char* sqlite3mcGetPageBuffer(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetLegacyReadCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetLegacyWriteCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetPageSizeReadCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetPageSizeWriteCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetReservedReadCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcGetReservedWriteCipher(Codec* codec);
SQLITE_PRIVATE int sqlite3mcReservedEqual(Codec* codec);
SQLITE_PRIVATE void sqlite3mcPadPassword(char* password, int pswdlen, unsigned char pswd[32]);
SQLITE_PRIVATE void sqlite3mcRC4(unsigned char* key, int keylen, unsigned char* textin, int textlen, unsigned char* textout);
SQLITE_PRIVATE void sqlite3mcGetMD5Binary(unsigned char* data, int length, unsigned char* digest);
SQLITE_PRIVATE void sqlite3mcGetSHABinary(unsigned char* data, int length, unsigned char* digest);
SQLITE_PRIVATE void sqlite3mcGenerateInitialVector(int seed, unsigned char iv[16]);
SQLITE_PRIVATE int sqlite3mcIsHexKey(const unsigned char* hex, int len);
SQLITE_PRIVATE int sqlite3mcConvertHex2Int(char c);
SQLITE_PRIVATE void sqlite3mcConvertHex2Bin(const unsigned char* hex, int len, unsigned char* bin);
SQLITE_PRIVATE int sqlite3mcConfigureFromUri(sqlite3* db, const char *zDbName, int configDefault);
SQLITE_PRIVATE void sqlite3mcConfigureSQLCipherVersion(sqlite3* db, int configDefault, int legacyVersion);
SQLITE_PRIVATE int sqlite3mcCodecAttach(sqlite3* db, int nDb, const char* zPath, const void* zKey, int nKey);
SQLITE_PRIVATE void sqlite3mcCodecGetKey(sqlite3* db, int nDb, void** zKey, int* nKey);
/* Debugging */
#if 0
#define SQLITE3MC_DEBUG
#define SQLITE3MC_DEBUG_DATA
#endif
#ifdef SQLITE3MC_DEBUG
#define SQLITE3MC_DEBUG_LOG(...) { fprintf(stdout, __VA_ARGS__); fflush(stdout); }
#else
#define SQLITE3MC_DEBUG_LOG(...)
#endif
#ifdef SQLITE3MC_DEBUG_DATA
#define SQLITE3MC_DEBUG_HEX(DESC,BUFFER,LEN) \
{ \
int count; \
printf(DESC); \
for (count = 0; count < LEN; ++count) \
{ \
if (count % 16 == 0) printf("\n%05x: ", count); \
printf("%02x ", ((unsigned char*) BUFFER)[count]); \
} \
printf("\n"); \
fflush(stdout); \
}
#else
#define SQLITE3MC_DEBUG_HEX(DESC,BUFFER,LEN)
#endif
#endif

View File

@ -1,7 +1,10 @@
@echo off
set DST2=..\..\..\lib2\static\delphi\sqlite3.obj
attrib -r ..\sqlite3.obj
del ..\sqlite3.obj
del %DST2%
set bcc=d:\dev\DelphiXE7
rem set bcc=d:\dev\bcc
@ -10,9 +13,9 @@ echo ---------------------------------------------------
echo Compiling for Delphi Win32 using %bcc%
%bcc%\bin\bcc32 -6 -Oi -O2 -c -d -u- sqlite3mc.c
copy sqlite3mc.obj ..\sqlite3.obj
del sqlite3mc.obj
copy sqlite3mc.obj ..\sqlite3.obj
copy sqlite3mc.obj %DST2%
attrib +r ..\sqlite3.obj
rem pause

View File

@ -1,17 +1,21 @@
@echo off
set DST2=..\..\..\lib2\static\delphi\sqlite3.o
attrib -r ..\sqlite3.o
del ..\sqlite3.o
del %DST2%
set bcc=d:\dev\DelphiXE7
rem set bcc=d:\Dev\bcc64ce
rem set bcc=d:\Dev\bcc\bcc64ce
echo ---------------------------------------------------
echo Compiling for Delphi Win64 using %bcc%
%bcc%\bin\bcc64 -isystem "%bcc%\include" -isystem "%bcc%\include\windows\sdk" -isystem "%bcc%\include\dinkumware64" -isystem "%bcc%\include\windows\crtl" -O2 -c -DWIN64 sqlite3mc.c
%bcc%\bin\bcc64 -Wno-pointer-sign -isystem "%bcc%\include" -isystem "%bcc%\include\windows\sdk" -isystem "%bcc%\include\dinkumware64" -isystem "%bcc%\include\windows\crtl" -O2 -c -DWIN64 sqlite3mc.c
copy sqlite3mc.o ..\sqlite3.o
del sqlite3mc.o
copy sqlite3mc.o %DST2%
attrib +r ..\sqlite3.o
rem pause

View File

@ -0,0 +1,25 @@
#!/bin/sh
ARCH=aarch64-android
CROSS=/home/ab/fpcup/cross/bin/all-android/bin
GCC=$CROSS/clang
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC --target=aarch64-linux-android21 -static -fPIC -Wno-pointer-sign -O2 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$ARCH.o
# -fPIC is needed for proper linking
#$CROSS/llvm-strip sqlite3-$ARCH.o
# striping remove all exported symbols :(
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2

View File

@ -5,13 +5,19 @@ ARCH=aarch64-linux
CROSS=/home/ab/fpcup/cross/bin/$ARCH
GCC=$CROSS/$ARCH-gcc
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC -static -O1 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
$CROSS/$ARCH-strip -d -x sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2

View File

@ -0,0 +1,24 @@
#!/bin/sh
ARCH=arm-android
CROSS=/home/ab/fpcup/cross/bin/all-android
GCC=$CROSS/bin/clang
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC --target=armv7a-linux-androideabi21 -static -O2 -Wno-pointer-sign -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -D__ARM_PCS_VFP -c sqlite3mc.c -o sqlite3-$ARCH.o
#$CROSS/bin/llvm-strip sqlite3-$ARCH.o
# striping remove all exported symbols :(
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2

View File

@ -5,13 +5,19 @@ ARCH=arm-linux
CROSS=/home/ab/fpcup/cross/bin/$ARCH
GCC=$CROSS/arm-linux-gnueabihf-gcc
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC -static -O1 -marm -march=armv7-a+fp -I$CROSS/include -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -D__ARM_PCS_VFP -mfloat-abi=hard -c sqlite3mc.c -o sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
$CROSS/arm-linux-gnueabihf-strip -d -x sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2

View File

@ -0,0 +1,24 @@
#!/bin/sh
ARCH=i386-android
CROSS=/home/ab/fpcup/cross/bin/all-android
GCC=$CROSS/bin/clang
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC --target=i686-linux-androideabi21 -static -Wno-pointer-sign -O2 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -D__ARM_PCS_VFP -c sqlite3mc.c -o sqlite3-$ARCH.o
#$CROSS/bin/llvm-strip sqlite3-$ARCH.o
# strip blows all external symbols
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2

View File

@ -2,18 +2,24 @@
ARCH=i386-darwin
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
CROSS=/home/ab/fpcup/cross
#CROSS=/home/ab/fpcup/cross
# use older but working fpcupdeluxe cross compiler
CROSS=/home/abouchez/fpcupdeluxe/__darwin
SDK=$CROSS/lib/x86-darwin/MacOSX10.11.sdk\usr
GCC=$CROSS/bin/x86-darwin/i386-apple-darwin15
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC-clang -static -target i386-apple-darwin15 -O2 -m32 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -I$SDK/include -c sqlite3mc.c -o sqlite3-$ARCH.o
$GCC-clang -static -target i386-apple-darwin15 -Wno-pointer-sign -O2 -m32 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -I$SDK/include -c sqlite3mc.c -o sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2
$GCC-libtool -static sqlite3-$ARCH.o -o ../../static/$ARCH/libsqlite3.a
$GCC-libtool -static sqlite3-$ARCH.o -o ../../../lib2/static/$ARCH/libsqlite3.a

View File

@ -5,13 +5,19 @@ FPCARCHVERSION=12
CROSS=/home/ab/fpcup/cross/bin/$FPCARCH
GCC=$CROSS/$FPCARCH$FPCARCHVERSION-gcc
DST=../../static/$FPCARCH/sqlite3.o
DST2=../../../lib2/static/$FPCARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$FPCARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $FPCARCH using $GCC
$GCC -static -O2 -m32 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
$CROSS/$FPCARCH$FPCARCHVERSION-strip -d -x sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
cp sqlite3-$FPCARCH.o $DST2

View File

@ -6,15 +6,19 @@ set GCCPATH=d:\fpcup\__win\bin\%FPCARCH%
set GCC=%FPCARCH%-gcc
set DST=..\..\static\%FPCARCH%\sqlite3.o
set DST2=..\..\..\lib2\static\%FPCARCH%\sqlite3.o
set path=%path%;%GCCPATH%
del %DST%
del %DST2%
del sqlite3-%FPCARCH%.o
echo.
echo ---------------------------------------------------
echo Compiling for FPC on %FPCARCH% using %GCC%
%GCC% -static -w -O2 -m32 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-%FPCARCH%.o
copy sqlite3-%FPCARCH%.o %DST%
copy sqlite3-%FPCARCH%.o %DST2%
rem pause

View File

@ -3,13 +3,19 @@
FPCARCH=i386-linux
GCC=gcc-7
DST=../../static/$FPCARCH/sqlite3.o
DST2=../../../lib2/static/$FPCARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$FPCARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $FPCARCH using $GCC
$GCC -static -O2 -m32 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
strip -d -x sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
cp sqlite3-$FPCARCH.o $DST2

View File

@ -5,13 +5,19 @@ FPCARCHVERSION=
CROSS=/home/ab/fpcup/cross/bin/$FPCARCH
GCC=$CROSS/$FPCARCH$FPCARCHVERSION-gcc
DST=../../static/$FPCARCH/sqlite3.o
DST2=../../../lib2/static/$FPCARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$FPCARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $FPCARCH using $GCC
$GCC -static -O2 -m32 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
$CROSS/$FPCARCH$FPCARCHVERSION-strip -d -x sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
cp sqlite3-$FPCARCH.o $DST2

View File

@ -3,13 +3,18 @@
ARCH=i386-win32
GCC=i686-w64-mingw32-gcc
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC -O2 -m32 -DWIN32 -DNDEBUG -D_WINDOWS -c sqlite3mc.c -o sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
i686-w64-mingw32-strip -d -x sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2

View File

@ -0,0 +1,24 @@
#!/bin/sh
ARCH=x86_64-android
CROSS=/home/ab/fpcup/cross/bin/all-android/bin
GCC=$CROSS/clang
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC --target=x86_64-linux-android21 -static -O2 -Wno-pointer-sign -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$ARCH.o
#$CROSS/llvm-strip sqlite3-$ARCH.o
# striping remove all exported symbols :(
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2

View File

@ -2,18 +2,24 @@
ARCH=x86_64-darwin
DST=../../static/$ARCH/sqlite3.o
DST2=../../../lib2/static/$ARCH/sqlite3.o
CROSS=/home/ab/fpcup/cross
#CROSS=/home/ab/fpcup/cross
# use older but working fpcupdeluxe cross compiler
CROSS=/home/abouchez/fpcupdeluxe/__darwin
SDK=$CROSS/lib/x86-darwin/MacOSX10.11.sdk\usr
GCC=$CROSS/bin/x86-darwin/x86_64-apple-darwin15
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $ARCH using $GCC
$GCC-clang -static -target x86_64-apple-darwin15 -O2 -m64 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -I$SDK/include -c sqlite3mc.c -o sqlite3-$ARCH.o
$GCC-clang -static -target x86_64-apple-darwin15 -O2 -m64 -Wno-pointer-sign -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -I$SDK/include -c sqlite3mc.c -o sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2
$GCC-libtool -static sqlite3-$ARCH.o -o ../../static/$ARCH/libsqlite3.a
$GCC-libtool -static sqlite3-$ARCH.o -o ../../../lib2/static/$ARCH/libsqlite3.a

View File

@ -5,13 +5,19 @@ FPCARCHVERSION=12
CROSS=/home/ab/fpcup/cross/bin/$FPCARCH
GCC=$CROSS/$FPCARCH$FPCARCHVERSION-gcc
DST=../../static/$FPCARCH/sqlite3.o
DST2=../../../lib2/static/$FPCARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$FPCARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $FPCARCH using $GCC
$GCC -static -O2 -m64 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
$CROSS/$FPCARCH$FPCARCHVERSION-strip -d -x sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
cp sqlite3-$FPCARCH.o $DST2

View File

@ -6,15 +6,21 @@ set GCCPATH=d:\fpcup\__win\bin\%FPCARCH%
set GCC=%FPCARCH%-gcc
set DST=..\..\static\%FPCARCH%\sqlite3.o
set DST2=..\..\..\lib2\static\%FPCARCH%\sqlite3.o
set path=%path%;%GCCPATH%
del %DST%
del %DST2%
del sqlite3-%FPCARCH%.o
echo.
echo ---------------------------------------------------
echo Compiling for FPC on %FPCARCH% using %GCC%
%GCC% -static -w -O2 -fno-pic -fno-stack-protector -m64 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-%FPCARCH%.o
%GCC% -static -w -O2 -fno-pic -fno-stack-protector -fomit-frame-pointer -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables -m64 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-%FPCARCH%.o
%FPCARCH%-strip -x sqlite3-%FPCARCH%.o
copy sqlite3-%FPCARCH%.o %DST%
copy sqlite3-%FPCARCH%.o %DST2%
rem pause

View File

@ -3,13 +3,18 @@
FPCARCH=x86_64-linux
GCC=gcc-7
DST=../../static/$FPCARCH/sqlite3.o
DST2=../../../lib2/static/$FPCARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$FPCARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $FPCARCH using $GCC
$GCC -static -fno-pic -fno-stack-protector -O2 -m64 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
strip -d -x sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
cp sqlite3-$FPCARCH.o $DST2

View File

@ -5,13 +5,19 @@ FPCARCHVERSION=
CROSS=/home/ab/fpcup/cross/bin/$FPCARCH
GCC=$CROSS/$FPCARCH$FPCARCHVERSION-gcc
DST=../../static/$FPCARCH/sqlite3.o
DST2=../../../lib2/static/$FPCARCH/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$FPCARCH.o
echo
echo ---------------------------------------------------
echo Compiling for FPC on $FPCARCH using $GCC
$GCC -static -O2 -m64 -DNDEBUG -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -c sqlite3mc.c -o sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
$CROSS/$FPCARCH$FPCARCHVERSION-strip -d -x sqlite3-$FPCARCH.o
cp sqlite3-$FPCARCH.o $DST
cp sqlite3-$FPCARCH.o $DST2

View File

@ -3,25 +3,40 @@
ARCH=x86_64-win64
GCC=x86_64-w64-mingw32-gcc
STATIC=../../static/$ARCH
LIB2=../../../lib2/static
STATIC2=$LIB2/$ARCH
STATIC2DELPHI=$LIB2/delphi
DST=$STATIC/sqlite3.o
DST2=$STATIC2/sqlite3.o
rm $DST
rm $DST2
rm sqlite3-$ARCH.o
echo
echo ---------------------------------------------------
echo Compiling static for FPC on $ARCH using $GCC
$GCC -O2 -static -DWIN64 -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -m64 -DNDEBUG -D_WINDOWS -c sqlite3mc.c -o sqlite3-$ARCH.o
x86_64-w64-mingw32-strip -d -x sqlite3-$ARCH.o
cp sqlite3-$ARCH.o $DST
cp sqlite3-$ARCH.o $DST2
DLL=sqlite3-64.dll
rm $DLL
rm $STATIC/$DLL
rm $STATIC2DELPHI/$DLL
A=libsqlite3-64.a
rm $STATIC/$A
rm $STATIC2/$A
echo
echo ---------------------------------------------------
echo Compiling $DLL using $GCC
$GCC -O2 -shared -DSQLITE_MMAP_READWRITE -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS4 -DDSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_DESERIALIZE -DWIN64 -DNDEBUG -D_WINDOWS -D_USRDLL -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_THREADSAFE=1 -DTEMP_STORE=1 -m64 sqlite3.c -o $DLL -Wl,--out-implib,libsqlite3-64.a
$GCC -O2 -shared -DSQLITE_MMAP_READWRITE -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_RBU -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_DESERIALIZE -DWIN64 -DNDEBUG -D_WINDOWS -D_USRDLL -DNO_TCL -D_CRT_SECURE_NO_DEPRECATE -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_THREADSAFE=1 -DTEMP_STORE=1 -m64 sqlite3.c -o $DLL -Wl,--out-implib,libsqlite3-64.a
cp $DLL $STATIC
cp libsqlite3-64.a $STATIC
cp $DLL $STATIC2DELPHI
cp $A $STATIC
cp $A $STATIC2

View File

@ -49,3 +49,11 @@ echo Use Native fpcupdeluxe cross-compilers for FPC OpenBSD i386/x64
./compile-fpc-i386-openbsd.sh
./compile-fpc-x86_64-openbsd.sh
echo
echo Use Native fpcupdeluxe cross-compilers for FPC Android arm/aarch64/i386/x64-android
./compile-fpc-arm-android.sh
./compile-fpc-aarch64-android.sh
./compile-fpc-i386-android.sh
./compile-fpc-x86_64-android.sh

View File

@ -0,0 +1,760 @@
/*
** 2012-11-13
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** The code in this file implements a compact but reasonably
** efficient regular-expression matcher for posix extended regular
** expressions against UTF8 text.
**
** This file is an SQLite extension. It registers a single function
** named "regexp(A,B)" where A is the regular expression and B is the
** string to be matched. By registering this function, SQLite will also
** then implement the "B regexp A" operator. Note that with the function
** the regular expression comes first, but with the operator it comes
** second.
**
** The following regular expression syntax is supported:
**
** X* zero or more occurrences of X
** X+ one or more occurrences of X
** X? zero or one occurrences of X
** X{p,q} between p and q occurrences of X
** (X) match X
** X|Y X or Y
** ^X X occurring at the beginning of the string
** X$ X occurring at the end of the string
** . Match any single character
** \c Character c where c is one of \{}()[]|*+?.
** \c C-language escapes for c in afnrtv. ex: \t or \n
** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
** \xXX Where XX is exactly 2 hex digits, unicode value XX
** [abc] Any single character from the set abc
** [^abc] Any single character not in the set abc
** [a-z] Any single character in the range a-z
** [^a-z] Any single character not in the range a-z
** \b Word boundary
** \w Word character. [A-Za-z0-9_]
** \W Non-word character
** \d Digit
** \D Non-digit
** \s Whitespace character
** \S Non-whitespace character
**
** A nondeterministic finite automaton (NFA) is used for matching, so the
** performance is bounded by O(N*M) where N is the size of the regular
** expression and M is the size of the input string. The matcher never
** exhibits exponential behavior. Note that the X{p,q} operator expands
** to p copies of X following by q-p copies of X? and that the size of the
** regular expression in the O(N*M) performance bound is computed after
** this expansion.
*/
#include <string.h>
#include <stdlib.h>
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
/*
** The following #defines change the names of some functions implemented in
** this file to prevent name collisions with C-library functions of the
** same name.
*/
#define re_match sqlite3re_match
#define re_compile sqlite3re_compile
#define re_free sqlite3re_free
/* The end-of-input character */
#define RE_EOF 0 /* End of input */
/* The NFA is implemented as sequence of opcodes taken from the following
** set. Each opcode has a single integer argument.
*/
#define RE_OP_MATCH 1 /* Match the one character in the argument */
#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
#define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */
#define RE_OP_GOTO 5 /* Jump to opcode at iArg */
#define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */
#define RE_OP_CC_INC 7 /* Beginning of a [...] character class */
#define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */
#define RE_OP_CC_VALUE 9 /* Single value in a character class */
#define RE_OP_CC_RANGE 10 /* Range of values in a character class */
#define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */
#define RE_OP_NOTWORD 12 /* Not a perl word character */
#define RE_OP_DIGIT 13 /* digit: [0-9] */
#define RE_OP_NOTDIGIT 14 /* Not a digit */
#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
#define RE_OP_NOTSPACE 16 /* Not a digit */
#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
/* Each opcode is a "state" in the NFA */
typedef unsigned short ReStateNumber;
/* Because this is an NFA and not a DFA, multiple states can be active at
** once. An instance of the following object records all active states in
** the NFA. The implementation is optimized for the common case where the
** number of actives states is small.
*/
typedef struct ReStateSet {
unsigned nState; /* Number of current states */
ReStateNumber *aState; /* Current states */
} ReStateSet;
/* An input string read one character at a time.
*/
typedef struct ReInput ReInput;
struct ReInput {
const unsigned char *z; /* All text */
int i; /* Next byte to read */
int mx; /* EOF when i>=mx */
};
/* A compiled NFA (or an NFA that is in the process of being compiled) is
** an instance of the following object.
*/
typedef struct ReCompiled ReCompiled;
struct ReCompiled {
ReInput sIn; /* Regular expression text */
const char *zErr; /* Error message to return */
char *aOp; /* Operators for the virtual machine */
int *aArg; /* Arguments to each operator */
unsigned (*xNextChar)(ReInput*); /* Next character function */
unsigned char zInit[12]; /* Initial text to match */
int nInit; /* Number of characters in zInit */
unsigned nState; /* Number of entries in aOp[] and aArg[] */
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
};
/* Add a state to the given state set if it is not already there */
static void re_add_state(ReStateSet *pSet, int newState){
unsigned i;
for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
pSet->aState[pSet->nState++] = (ReStateNumber)newState;
}
/* Extract the next unicode character from *pzIn and return it. Advance
** *pzIn to the first byte past the end of the character returned. To
** be clear: this routine converts utf8 to unicode. This routine is
** optimized for the common case where the next character is a single byte.
*/
static unsigned re_next_char(ReInput *p){
unsigned c;
if( p->i>=p->mx ) return 0;
c = p->z[p->i++];
if( c>=0x80 ){
if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){
c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
if( c<0x80 ) c = 0xfffd;
}else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
&& (p->z[p->i+1]&0xc0)==0x80 ){
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
p->i += 2;
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
}else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
| (p->z[p->i+2]&0x3f);
p->i += 3;
if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
}else{
c = 0xfffd;
}
}
return c;
}
static unsigned re_next_char_nocase(ReInput *p){
unsigned c = re_next_char(p);
if( c>='A' && c<='Z' ) c += 'a' - 'A';
return c;
}
/* Return true if c is a perl "word" character: [A-Za-z0-9_] */
static int re_word_char(int c){
return (c>='0' && c<='9') || (c>='a' && c<='z')
|| (c>='A' && c<='Z') || c=='_';
}
/* Return true if c is a "digit" character: [0-9] */
static int re_digit_char(int c){
return (c>='0' && c<='9');
}
/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
static int re_space_char(int c){
return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
}
/* Run a compiled regular expression on the zero-terminated input
** string zIn[]. Return true on a match and false if there is no match.
*/
static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
ReStateSet aStateSet[2], *pThis, *pNext;
ReStateNumber aSpace[100];
ReStateNumber *pToFree;
unsigned int i = 0;
unsigned int iSwap = 0;
int c = RE_EOF+1;
int cPrev = 0;
int rc = 0;
ReInput in;
in.z = zIn;
in.i = 0;
in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
/* Look for the initial prefix match, if there is one. */
if( pRe->nInit ){
unsigned char x = pRe->zInit[0];
while( in.i+pRe->nInit<=in.mx
&& (zIn[in.i]!=x ||
strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
){
in.i++;
}
if( in.i+pRe->nInit>in.mx ) return 0;
}
if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
pToFree = 0;
aStateSet[0].aState = aSpace;
}else{
pToFree = sqlite3_malloc64( sizeof(ReStateNumber)*2*pRe->nState );
if( pToFree==0 ) return -1;
aStateSet[0].aState = pToFree;
}
aStateSet[1].aState = &aStateSet[0].aState[pRe->nState];
pNext = &aStateSet[1];
pNext->nState = 0;
re_add_state(pNext, 0);
while( c!=RE_EOF && pNext->nState>0 ){
cPrev = c;
c = pRe->xNextChar(&in);
pThis = pNext;
pNext = &aStateSet[iSwap];
iSwap = 1 - iSwap;
pNext->nState = 0;
for(i=0; i<pThis->nState; i++){
int x = pThis->aState[i];
switch( pRe->aOp[x] ){
case RE_OP_MATCH: {
if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
break;
}
case RE_OP_ANY: {
re_add_state(pNext, x+1);
break;
}
case RE_OP_WORD: {
if( re_word_char(c) ) re_add_state(pNext, x+1);
break;
}
case RE_OP_NOTWORD: {
if( !re_word_char(c) ) re_add_state(pNext, x+1);
break;
}
case RE_OP_DIGIT: {
if( re_digit_char(c) ) re_add_state(pNext, x+1);
break;
}
case RE_OP_NOTDIGIT: {
if( !re_digit_char(c) ) re_add_state(pNext, x+1);
break;
}
case RE_OP_SPACE: {
if( re_space_char(c) ) re_add_state(pNext, x+1);
break;
}
case RE_OP_NOTSPACE: {
if( !re_space_char(c) ) re_add_state(pNext, x+1);
break;
}
case RE_OP_BOUNDARY: {
if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1);
break;
}
case RE_OP_ANYSTAR: {
re_add_state(pNext, x);
re_add_state(pThis, x+1);
break;
}
case RE_OP_FORK: {
re_add_state(pThis, x+pRe->aArg[x]);
re_add_state(pThis, x+1);
break;
}
case RE_OP_GOTO: {
re_add_state(pThis, x+pRe->aArg[x]);
break;
}
case RE_OP_ACCEPT: {
rc = 1;
goto re_match_end;
}
case RE_OP_CC_INC:
case RE_OP_CC_EXC: {
int j = 1;
int n = pRe->aArg[x];
int hit = 0;
for(j=1; j>0 && j<n; j++){
if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
if( pRe->aArg[x+j]==c ){
hit = 1;
j = -1;
}
}else{
if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){
hit = 1;
j = -1;
}else{
j++;
}
}
}
if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
if( hit ) re_add_state(pNext, x+n);
break;
}
}
}
}
for(i=0; i<pNext->nState; i++){
if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
}
re_match_end:
sqlite3_free(pToFree);
return rc;
}
/* Resize the opcode and argument arrays for an RE under construction.
*/
static int re_resize(ReCompiled *p, int N){
char *aOp;
int *aArg;
aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
if( aOp==0 ) return 1;
p->aOp = aOp;
aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
if( aArg==0 ) return 1;
p->aArg = aArg;
p->nAlloc = N;
return 0;
}
/* Insert a new opcode and argument into an RE under construction. The
** insertion point is just prior to existing opcode iBefore.
*/
static int re_insert(ReCompiled *p, int iBefore, int op, int arg){
int i;
if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
for(i=p->nState; i>iBefore; i--){
p->aOp[i] = p->aOp[i-1];
p->aArg[i] = p->aArg[i-1];
}
p->nState++;
p->aOp[iBefore] = (char)op;
p->aArg[iBefore] = arg;
return iBefore;
}
/* Append a new opcode and argument to the end of the RE under construction.
*/
static int re_append(ReCompiled *p, int op, int arg){
return re_insert(p, p->nState, op, arg);
}
/* Make a copy of N opcodes starting at iStart onto the end of the RE
** under construction.
*/
static void re_copy(ReCompiled *p, int iStart, int N){
if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
p->nState += N;
}
/* Return true if c is a hexadecimal digit character: [0-9a-fA-F]
** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If
** c is not a hex digit *pV is unchanged.
*/
static int re_hex(int c, int *pV){
if( c>='0' && c<='9' ){
c -= '0';
}else if( c>='a' && c<='f' ){
c -= 'a' - 10;
}else if( c>='A' && c<='F' ){
c -= 'A' - 10;
}else{
return 0;
}
*pV = (*pV)*16 + (c & 0xff);
return 1;
}
/* A backslash character has been seen, read the next character and
** return its interpretation.
*/
static unsigned re_esc_char(ReCompiled *p){
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
static const char zTrans[] = "\a\f\n\r\t\v";
int i, v = 0;
char c;
if( p->sIn.i>=p->sIn.mx ) return 0;
c = p->sIn.z[p->sIn.i];
if( c=='u' && p->sIn.i+4<p->sIn.mx ){
const unsigned char *zIn = p->sIn.z + p->sIn.i;
if( re_hex(zIn[1],&v)
&& re_hex(zIn[2],&v)
&& re_hex(zIn[3],&v)
&& re_hex(zIn[4],&v)
){
p->sIn.i += 5;
return v;
}
}
if( c=='x' && p->sIn.i+2<p->sIn.mx ){
const unsigned char *zIn = p->sIn.z + p->sIn.i;
if( re_hex(zIn[1],&v)
&& re_hex(zIn[2],&v)
){
p->sIn.i += 3;
return v;
}
}
for(i=0; zEsc[i] && zEsc[i]!=c; i++){}
if( zEsc[i] ){
if( i<6 ) c = zTrans[i];
p->sIn.i++;
}else{
p->zErr = "unknown \\ escape";
}
return c;
}
/* Forward declaration */
static const char *re_subcompile_string(ReCompiled*);
/* Peek at the next byte of input */
static unsigned char rePeek(ReCompiled *p){
return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0;
}
/* Compile RE text into a sequence of opcodes. Continue up to the
** first unmatched ")" character, then return. If an error is found,
** return a pointer to the error message string.
*/
static const char *re_subcompile_re(ReCompiled *p){
const char *zErr;
int iStart, iEnd, iGoto;
iStart = p->nState;
zErr = re_subcompile_string(p);
if( zErr ) return zErr;
while( rePeek(p)=='|' ){
iEnd = p->nState;
re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart);
iGoto = re_append(p, RE_OP_GOTO, 0);
p->sIn.i++;
zErr = re_subcompile_string(p);
if( zErr ) return zErr;
p->aArg[iGoto] = p->nState - iGoto;
}
return 0;
}
/* Compile an element of regular expression text (anything that can be
** an operand to the "|" operator). Return NULL on success or a pointer
** to the error message if there is a problem.
*/
static const char *re_subcompile_string(ReCompiled *p){
int iPrev = -1;
int iStart;
unsigned c;
const char *zErr;
while( (c = p->xNextChar(&p->sIn))!=0 ){
iStart = p->nState;
switch( c ){
case '|':
case '$':
case ')': {
p->sIn.i--;
return 0;
}
case '(': {
zErr = re_subcompile_re(p);
if( zErr ) return zErr;
if( rePeek(p)!=')' ) return "unmatched '('";
p->sIn.i++;
break;
}
case '.': {
if( rePeek(p)=='*' ){
re_append(p, RE_OP_ANYSTAR, 0);
p->sIn.i++;
}else{
re_append(p, RE_OP_ANY, 0);
}
break;
}
case '*': {
if( iPrev<0 ) return "'*' without operand";
re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1);
re_append(p, RE_OP_FORK, iPrev - p->nState + 1);
break;
}
case '+': {
if( iPrev<0 ) return "'+' without operand";
re_append(p, RE_OP_FORK, iPrev - p->nState);
break;
}
case '?': {
if( iPrev<0 ) return "'?' without operand";
re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
break;
}
case '{': {
int m = 0, n = 0;
int sz, j;
if( iPrev<0 ) return "'{m,n}' without operand";
while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
n = m;
if( c==',' ){
p->sIn.i++;
n = 0;
while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
}
if( c!='}' ) return "unmatched '{'";
if( n>0 && n<m ) return "n less than m in '{m,n}'";
p->sIn.i++;
sz = p->nState - iPrev;
if( m==0 ){
if( n==0 ) return "both m and n are zero in '{m,n}'";
re_insert(p, iPrev, RE_OP_FORK, sz+1);
n--;
}else{
for(j=1; j<m; j++) re_copy(p, iPrev, sz);
}
for(j=m; j<n; j++){
re_append(p, RE_OP_FORK, sz+1);
re_copy(p, iPrev, sz);
}
if( n==0 && m>0 ){
re_append(p, RE_OP_FORK, -sz);
}
break;
}
case '[': {
int iFirst = p->nState;
if( rePeek(p)=='^' ){
re_append(p, RE_OP_CC_EXC, 0);
p->sIn.i++;
}else{
re_append(p, RE_OP_CC_INC, 0);
}
while( (c = p->xNextChar(&p->sIn))!=0 ){
if( c=='[' && rePeek(p)==':' ){
return "POSIX character classes not supported";
}
if( c=='\\' ) c = re_esc_char(p);
if( rePeek(p)=='-' ){
re_append(p, RE_OP_CC_RANGE, c);
p->sIn.i++;
c = p->xNextChar(&p->sIn);
if( c=='\\' ) c = re_esc_char(p);
re_append(p, RE_OP_CC_RANGE, c);
}else{
re_append(p, RE_OP_CC_VALUE, c);
}
if( rePeek(p)==']' ){ p->sIn.i++; break; }
}
if( c==0 ) return "unclosed '['";
p->aArg[iFirst] = p->nState - iFirst;
break;
}
case '\\': {
int specialOp = 0;
switch( rePeek(p) ){
case 'b': specialOp = RE_OP_BOUNDARY; break;
case 'd': specialOp = RE_OP_DIGIT; break;
case 'D': specialOp = RE_OP_NOTDIGIT; break;
case 's': specialOp = RE_OP_SPACE; break;
case 'S': specialOp = RE_OP_NOTSPACE; break;
case 'w': specialOp = RE_OP_WORD; break;
case 'W': specialOp = RE_OP_NOTWORD; break;
}
if( specialOp ){
p->sIn.i++;
re_append(p, specialOp, 0);
}else{
c = re_esc_char(p);
re_append(p, RE_OP_MATCH, c);
}
break;
}
default: {
re_append(p, RE_OP_MATCH, c);
break;
}
}
iPrev = iStart;
}
return 0;
}
/* Free and reclaim all the memory used by a previously compiled
** regular expression. Applications should invoke this routine once
** for every call to re_compile() to avoid memory leaks.
*/
static void re_free(ReCompiled *pRe){
if( pRe ){
sqlite3_free(pRe->aOp);
sqlite3_free(pRe->aArg);
sqlite3_free(pRe);
}
}
/*
** Compile a textual regular expression in zIn[] into a compiled regular
** expression suitable for us by re_match() and return a pointer to the
** compiled regular expression in *ppRe. Return NULL on success or an
** error message if something goes wrong.
*/
static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
ReCompiled *pRe;
const char *zErr;
int i, j;
*ppRe = 0;
pRe = sqlite3_malloc( sizeof(*pRe) );
if( pRe==0 ){
return "out of memory";
}
memset(pRe, 0, sizeof(*pRe));
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
if( re_resize(pRe, 30) ){
re_free(pRe);
return "out of memory";
}
if( zIn[0]=='^' ){
zIn++;
}else{
re_append(pRe, RE_OP_ANYSTAR, 0);
}
pRe->sIn.z = (unsigned char*)zIn;
pRe->sIn.i = 0;
pRe->sIn.mx = (int)strlen(zIn);
zErr = re_subcompile_re(pRe);
if( zErr ){
re_free(pRe);
return zErr;
}
if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
re_append(pRe, RE_OP_MATCH, RE_EOF);
re_append(pRe, RE_OP_ACCEPT, 0);
*ppRe = pRe;
}else if( pRe->sIn.i>=pRe->sIn.mx ){
re_append(pRe, RE_OP_ACCEPT, 0);
*ppRe = pRe;
}else{
re_free(pRe);
return "unrecognized character";
}
/* The following is a performance optimization. If the regex begins with
** ".*" (if the input regex lacks an initial "^") and afterwards there are
** one or more matching characters, enter those matching characters into
** zInit[]. The re_match() routine can then search ahead in the input
** string looking for the initial match without having to run the whole
** regex engine over the string. Do not worry able trying to match
** unicode characters beyond plane 0 - those are very rare and this is
** just an optimization. */
if( pRe->aOp[0]==RE_OP_ANYSTAR ){
for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
unsigned x = pRe->aArg[i];
if( x<=127 ){
pRe->zInit[j++] = (unsigned char)x;
}else if( x<=0xfff ){
pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
pRe->zInit[j++] = 0x80 | (x&0x3f);
}else if( x<=0xffff ){
pRe->zInit[j++] = (unsigned char)(0xd0 | (x>>12));
pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
pRe->zInit[j++] = 0x80 | (x&0x3f);
}else{
break;
}
}
if( j>0 && pRe->zInit[j-1]==0 ) j--;
pRe->nInit = j;
}
return pRe->zErr;
}
/*
** Implementation of the regexp() SQL function. This function implements
** the build-in REGEXP operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
**
** A REGEXP B
**
** is implemented as regexp(B,A).
*/
static void re_sql_func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
ReCompiled *pRe; /* Compiled regular expression */
const char *zPattern; /* The regular expression */
const unsigned char *zStr;/* String being searched */
const char *zErr; /* Compile error message */
int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, 0);
if( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1);
return;
}
if( pRe==0 ){
sqlite3_result_error_nomem(context);
return;
}
setAux = 1;
}
zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
if( zStr!=0 ){
sqlite3_result_int(context, re_match(pRe, zStr, -1));
}
if( setAux ){
sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
}
}
/*
** Invoke this routine to register the regexp() function with the
** SQLite database connection.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_regexp_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
0, re_sql_func, 0, 0);
return rc;
}

View File

@ -1,53 +1,109 @@
/*
** Wrapper around SQlite3 amalgamation file with proper options and code
** Wrapper around SQlite3 amalgamation file for mORMot use
**
** Please download and put sqlite3.c in amalgamation/ sub-folder
** from https://sqlite.org/download.html
** then run ./patch.sh
** then follow the amalgamation/ReadMe.md instructions
*/
/*
** Define all symbols expected by SynSQLite3Static.pas
** Define conditionals / extensions specially tuned for mORMot
**
** See also https://www.sqlite.org/compile.html#recommended_compile_time_options
*/
#define SQLITE_DEFAULT_MEMSTATUS 0
// don't need any debug here, and don't even define sqlite3_status()
#define SQLITE_THREADSAFE 1
// assuming multi-thread safety is made by caller - in our framework, there is
// only one thread using the database connection at the same time, but there could
// be multiple database connection at the same time (previous was 0 could be unsafe)
// - this option is also needed by codecext.c
// be multiple database connection at the same time
// * 0 = single-thread = all mutexes disabled - seems unsafe
// * 1 = serialized = all calls serialized - seems overkill
// * 2 = multi-thread = thread-safe by connection - fine for our purpose
// - note that we keep 1=serialized at compile time (to allow all modes)
// but SQLITE_CONFIG_MULTITHREAD is set in TSqlite3Library.BeforeInitialization
// and rely on TSqlDataBase to do explicit Lock/LockJson/UnLock calls
#if SQLITE_NO_THREAD
# define SQLITE_THREADSAFE 0
#else
# define SQLITE_THREADSAFE 1
#endif
#define SQLITE_OMIT_SHARED_CACHE 1
// no need of shared cache in a threadsafe calling model
#define SQLITE_OMIT_AUTOINIT 1
// sqlite3_initialize() is done in unit initialization -> no AUTOINIT
// sqlite3_initialize() is done in unit initialization -> no AUTOINIT
#define SQLITE_OMIT_DEPRECATED 1
// spare some code size
// spare some code size
#define SQLITE_LIKE_DOESNT_MATCH_BLOBS 1
// historical function, never used
#define SQLITE_ENABLE_FTS3 1
#define SQLITE_ENABLE_FTS3_PARENTHESIS 1
#define SQLITE_ENABLE_FTS4 1
#define SQLITE_ENABLE_FTS5 1
// enable all FTS engines
#define SQLITE_ENABLE_RBU 1
// "Resumable Bulk Update" (or OTA) is not used/published yet
// enable all FTS engines https://www.sqlite.org/fts3.html https://www.sqlite.org/fts5.html
#define SQLITE_ENABLE_JSON1 1
// add JSON extension
// enable JSON https://www.sqlite.org/json1.html
#define SQLITE_MAX_EXPR_DEPTH 0
// no SQL depth limit, since we trust the input and expect the best performance
#define SQLITE_OMIT_LOAD_EXTENSION 1
// we don't need/allow extension in an embedded engine
#define SQLITE_OMIT_COMPILEOPTION_DIAGS 1
// we don't need Compilation Options Diagnostics in our embedded engine
#define SQLITE_OMIT_PROGRESS_CALLBACK 1
// we don't need sqlite3_progress_handler() API function
#define SQLITE_ENABLE_RTREE 1
// the RTREE extension is now (from v.1.8/3.7) compiled into the engine
#define SQLITE_ENABLE_DESERIALIZE
// enables sqlite3_serialize() and sqlite3_deserialize()
// enable sqlite3_serialize() and sqlite3_deserialize()
#define SQLITE_ENABLE_RTREE 1
// enable RTREE https://sqlite.org/rtree.html
#define SQLITE_ENABLE_GEOPOLY 1
// enable GeoJSON over RTREE https://sqlite.org/geopoly.html
#define SQLITE_ENABLE_REGEXP 1
// enable the compact https://www.sqlite.org/src/file?name=ext/misc/regexp.c
// - can be overloaded with any other implementation
#define SQLITE_ENABLE_RBU 1
// enable "Resumable Bulk Update" (or OTA) https://www.sqlite.org/rbu.html
#define SQLITE_ENABLE_SESSION 1
#define SQLITE_ENABLE_PREUPDATE_HOOK 1
// enable Sessions https://sqlite.org/sessionintro.html
#define SQLITE_ENABLE_NORMALIZE 1
// enable all https://sqlite.org/c3ref/expanded_sql.html functions
#define YYTRACKMAXSTACKDEPTH 1
// enable SQLITE_STATUS_PARSER_STACK support
#define SQLITE_ENABLE_COLUMN_METADATA 1
//enable column_database_name, column_table_name and column_origin_name support
#define SQLITE_ENABLE_STMT_SCANSTATUS 1
// enable stmt_scanstatus and stmt_scanstatus_reset support
#define SQLITE_ENABLE_SNAPSHOT 1
// support the sqlite3_snapshot object
#define SQLITE_ENABLE_UNLOCK_NOTIFY 1
// enable sqlite3_unlock_notify
/*
** Disabled conditionals / extensions
*/
// #define SQLITE_ENABLE_ICU
// disabled because induces a huge dependency - use WIN32NOCASE (which calls
// ICU on POSIX) or even better the UNICODENOCASE as available in mORMot 2
// #define SQLITE_ENABLE_STAT4
// adds additional logic to the ANALYZE command and to the Query Planner
/*
** Define function for extra initilization
@ -229,6 +285,18 @@ static unsigned char* CodecGetPageBuffer(Codec* codec)
#include "codecext.c"
/*
** REGEXP
*/
#ifdef SQLITE_ENABLE_REGEXP
/* Prototype for initialization function of REGEXP extension */
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_regexp_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);
#include "regexp.c"
#endif
/*
** Multi cipher VFS
*/
@ -259,6 +327,17 @@ sqlite3mc_initialize(const char* arg)
}
rc = sqlite3mc_vfs_initialize(vfsDefault, 1);
}
/*
** Can be overloaded later with any other REGEXP engine
*/
#ifdef SQLITE_ENABLE_REGEXP
if (rc == SQLITE_OK)
{
rc = sqlite3_auto_extension((void(*)(void)) sqlite3_regexp_init);
}
#endif
return rc;
}

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ unit mORMotBigTable;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotBigTable;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotDB;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotDB;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -79,7 +79,8 @@ type
// - Handled=FALSE would let the default ID computation take place
// - note that execution of this method would be protected by a mutex, so
// it would be thread-safe
TOnEngineAddComputeID = function(Sender: TSQLRestStorageExternal; var Handled: Boolean): TID of object;
TOnEngineAddComputeID = function(Sender: TSQLRestStorageExternal;
var Handled: Boolean): TID of object;
/// REST server with direct access to a SynDB-based external database
// - handle all REST commands, using the external SQL database connection,
@ -119,21 +120,25 @@ type
fBatchIDs: TIDDynArray;
/// get fFieldsExternal[] index using fFieldsExternalToInternal[] mapping
// - do handle ID/RowID fields and published methods
function InternalFieldNameToFieldExternalIndex(const InternalFieldName: RawUTF8): integer;
function InternalFieldNameToFieldExternalIndex(
const InternalFieldName: RawUTF8): integer;
/// create, prepare and bound inlined parameters to a thread-safe statement
// - this implementation will call the ThreadSafeConnection virtual method,
// then bound inlined parameters as :(1234): and call its Execute method
// - should return nil on error, and not raise an exception
function PrepareInlinedForRows(const aSQL: RawUTF8): ISQLDBStatement;
/// overloaded method using FormatUTF8() and binding SynDB parameters
function PrepareDirectForRows(SQLFormat: PUTF8Char; const Args, Params: array of const): ISQLDBStatement;
function PrepareDirectForRows(SQLFormat: PUTF8Char;
const Args, Params: array of const): ISQLDBStatement;
/// create, prepare, bound inlined parameters and execute a thread-safe statement
// - this implementation will call the ThreadSafeConnection virtual method,
// then bound inlined parameters as :(1234): and call its Execute method
// - should return nil on error, and not raise an exception
function ExecuteInlined(const aSQL: RawUTF8; ExpectResults: Boolean): ISQLDBRows; overload;
function ExecuteInlined(const aSQL: RawUTF8;
ExpectResults: Boolean): ISQLDBRows; overload;
/// overloaded method using FormatUTF8() and inlined parameters
function ExecuteInlined(SQLFormat: PUTF8Char; const Args: array of const; ExpectResults: Boolean): ISQLDBRows; overload;
function ExecuteInlined(SQLFormat: PUTF8Char; const Args: array of const;
ExpectResults: Boolean): ISQLDBRows; overload;
/// overloaded method using FormatUTF8() and binding SynDB parameters
function ExecuteDirect(SQLFormat: PUTF8Char; const Args, Params: array of const;
ExpectResults: Boolean): ISQLDBRows;
@ -146,10 +151,12 @@ type
function EngineExecute(const aSQL: RawUTF8): boolean; override;
function EngineLockedNextID: TID; virtual;
function EngineAdd(TableModelIndex: integer; const SentData: RawUTF8): TID; override;
function EngineUpdate(TableModelIndex: integer; ID: TID; const SentData: RawUTF8): boolean; override;
function EngineUpdate(TableModelIndex: integer; ID: TID; const
SentData: RawUTF8): boolean; override;
function EngineDeleteWhere(TableModelIndex: integer; const SQLWhere: RawUTF8;
const IDs: TIDDynArray): boolean; override;
function EngineList(const SQL: RawUTF8; ForceAJAX: Boolean=false; ReturnedRowCount: PPtrInt=nil): RawUTF8; override;
function EngineList(const SQL: RawUTF8; ForceAJAX: Boolean=false;
ReturnedRowCount: PPtrInt=nil): RawUTF8; override;
// BLOBs should be access directly, not through slower JSON Base64 encoding
function EngineRetrieveBlob(TableModelIndex: integer; aID: TID;
BlobField: PPropInfo; out BlobData: TSQLRawBlob): boolean; override;
@ -224,7 +231,8 @@ type
// - must be ended with Commit on success
// - must be aborted with Rollback if any SQL statement failed
// - return true if no transaction is active, false otherwise
function TransactionBegin(aTable: TSQLRecordClass; SessionID: cardinal=1): boolean; override;
function TransactionBegin(aTable: TSQLRecordClass;
SessionID: cardinal=1): boolean; override;
/// end a transaction (implements REST END Member)
// - write all pending SQL statements to the external database
procedure Commit(SessionID: cardinal=1; RaiseException: boolean=false); override;
@ -363,8 +371,8 @@ type
// and TSQLRestStorageExternal as the related static class
// - no particular class is supplied here, since it will depend on the
// associated Static TSQLRestStorageExternal instance
class procedure GetTableModuleProperties(var aProperties: TVirtualTableModuleProperties);
override;
class procedure GetTableModuleProperties(
var aProperties: TVirtualTableModuleProperties); override;
/// called to determine the best way to access the virtual table
// - will prepare the request for TSQLVirtualTableCursor.Search()
// - this overridden method will let the external DB engine perform the search,
@ -391,7 +399,8 @@ type
/// called to update a virtual table row content
// - column order follows the Structure method, i.e. StoredClassProps.Fields[] order
// - returns true on success, false otherwise
function Update(oldRowID, newRowID: Int64; var Values: TSQLVarDynArray): boolean; override;
function Update(oldRowID, newRowID: Int64;
var Values: TSQLVarDynArray): boolean; override;
end;
@ -440,7 +449,8 @@ function VirtualTableExternalRegister(aModel: TSQLModel;
// definitions, in a fluent interface:
function VirtualTableExternalMap(aModel: TSQLModel;
aClass: TSQLRecordClass; aExternalDB: TSQLDBConnectionProperties;
const aExternalTableName: RawUTF8=''; aMapping: TSQLRecordPropertiesMappingOptions=[]): PSQLRecordPropertiesMapping;
const aExternalTableName: RawUTF8='';
aMapping: TSQLRecordPropertiesMappingOptions=[]): PSQLRecordPropertiesMapping;
type
/// all possible options for VirtualTableExternalRegisterAll/TSQLRestExternalDBCreate
@ -505,6 +515,7 @@ function TSQLRestExternalDBCreate(aModel: TSQLModel;
implementation
function VirtualTableExternalRegister(aModel: TSQLModel; aClass: TSQLRecordClass;
aExternalDB: TSQLDBConnectionProperties; const aExternalTableName: RawUTF8;
aMappingOptions: TSQLRecordPropertiesMappingOptions): boolean;
@ -577,7 +588,8 @@ end;
function VirtualTableExternalMap(aModel: TSQLModel;
aClass: TSQLRecordClass; aExternalDB: TSQLDBConnectionProperties;
const aExternalTableName: RawUTF8; aMapping: TSQLRecordPropertiesMappingOptions): PSQLRecordPropertiesMapping;
const aExternalTableName: RawUTF8;
aMapping: TSQLRecordPropertiesMappingOptions): PSQLRecordPropertiesMapping;
begin
if VirtualTableExternalRegister(aModel,aClass,aExternalDB,aExternalTableName,aMapping) then
result := @aModel.Props[aClass].ExternalDB else
@ -657,7 +669,7 @@ constructor TSQLRestStorageExternal.Create(aClass: TSQLRecordClass;
{$ifndef NOVARIANTS}
ftUTF8, // sftVariant
ftUTF8, // sftNullable (retrieved from Prop.SQLFieldTypeStored)
{$endif}
{$endif NOVARIANTS}
ftBlob, // sftBlob
ftBlob, // sftBlobDynArray
ftBlob, // sftBlobCustom
@ -987,6 +999,7 @@ function TSQLRestStorageExternal.EngineLockedNextID: TID;
if (rows<>nil) and rows.Step then
fEngineLockedMaxID := rows.ColumnInt(0) else
fEngineLockedMaxID := 0;
rows.ReleaseRows;
end;
var handled: boolean;
@ -1348,8 +1361,10 @@ begin
result := false else begin
rows := ExecuteDirect(pointer(fSelectTableHasRowsSQL),[],[],true);
if rows=nil then
result := false else
result := false else begin
result := rows.Step;
rows.ReleaseRows;
end;
end;
end;
@ -1359,9 +1374,13 @@ begin
if (self=nil) or (Table<>fStoredClass) then
result := 0 else begin
rows := ExecuteDirect('select count(*) from %',[fTableName],[],true);
if (rows=nil) or not rows.Step then
result := 0 else
result := rows.ColumnInt(0);
if rows=nil then
result := 0 else begin
if not rows.Step then
result := 0 else
result := rows.ColumnInt(0);
rows.ReleaseRows;
end;
end;
end;

View File

@ -6,7 +6,7 @@ unit mORMotDDD;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotDDD;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -2540,7 +2540,7 @@ begin
end;
writeln('Press [Enter] to quit');
ioresult;
readln;
ConsoleWaitForEnterKey; // for proper Synchronize() work
{$ifdef LINUX}
if ioresult<>0 then // e.g. when redirected from "nohup daemon &" command
WaitUntilHalted;

View File

@ -6,7 +6,7 @@ unit mORMotFastCgiServer;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotFastCgiServer;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotHttpClient;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotHttpClient;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -580,17 +580,17 @@ begin
Create(URI.Server,URI.Port,aModel,URI.Https);
P := Pointer(aDefinition.DataBaseName);
while P<>nil do begin
if UrlDecodeCardinal(P,'CONNECTTIMEOUT',V) then
if UrlDecodeCardinal(P,'CONNECTTIMEOUT=',V) then
fConnectTimeout := V else
if UrlDecodeCardinal(P,'SENDTIMEOUT',V) then
if UrlDecodeCardinal(P,'SENDTIMEOUT=',V) then
fSendTimeout := V else
if UrlDecodeCardinal(P,'RECEIVETIMEOUT',V) then
if UrlDecodeCardinal(P,'RECEIVETIMEOUT=',V) then
fReceiveTimeout := V else
if UrlDecodeValue(P,'PROXYNAME',tmp) then
if UrlDecodeValue(P,'PROXYNAME=',tmp) then
fProxyName := CurrentAnsiConvert.UTF8ToAnsi(tmp) else
if UrlDecodeValue(P,'PROXYBYPASS',tmp) then
if UrlDecodeValue(P,'PROXYBYPASS=',tmp) then
fProxyByPass := CurrentAnsiConvert.UTF8ToAnsi(tmp);
if UrlDecodeCardinal(P,'IGNORESSLCERTIFICATEERRORS',V,@P) then
if UrlDecodeCardinal(P,'IGNORESSLCERTIFICATEERRORS=',V,@P) then
fExtendedOptions.IgnoreSSLCertificateErrors := Boolean(V);
end;
inherited RegisteredClassCreateFrom(aModel,aDefinition); // call SetUser()

View File

@ -6,7 +6,7 @@ unit mORMotHttpServer;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotHttpServer;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -577,7 +577,6 @@ begin
if aURI='' then
fHosts.Delete(aDomain) else
fHosts.Add(aDomain,aURI); // e.g. Add('project1.com','root1')
end;
constructor TSQLHttpServer.Create(const aPort: AnsiString;
@ -925,7 +924,7 @@ begin
if fRedirectServerRootUriForExactCase and (match=rmMatchWithCaseChange) then begin
// force redirection to exact Server.Model.Root case sensitivity
call.OutStatus := HTTP_TEMPORARYREDIRECT;
call.OutHead := 'Location: '+serv.Model.Root+
call.OutHead := 'Location: /'+serv.Model.Root+
copy(call.Url,length(serv.Model.Root)+1,maxInt);
end else begin
// call matching TSQLRestServer.URI()
@ -950,6 +949,8 @@ begin
if hostroot<>'' then begin
if ((result=HTTP_MOVEDPERMANENTLY) or (result=HTTP_TEMPORARYREDIRECT)) then begin
redirect := FindIniNameValue(P,'LOCATION: ');
if (redirect<>'') and (redirect[1]='/') then
delete(redirect,1,1); // what is needed for real URI doesn't help here
hostlen := length(hostroot);
if (length(redirect)>hostlen) and (redirect[hostlen+1]='/') and
IdemPropNameU(hostroot,pointer(redirect),hostlen) then

View File

@ -6,7 +6,7 @@ unit mORMotMVC;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotMVC;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -84,7 +84,7 @@ type
TMVCViewFlags = set of (viewHasGenerationTimeTag);
/// define a particular rendered View
// - as rendered by TMVCViewsAbtract.Render() method
// - as rendered by TMVCViewsAbstract.Render() method
TMVCView = record
/// the low-level content of this View
Content: RawByteString;
@ -95,7 +95,7 @@ type
end;
/// an abstract class able to implement Views
TMVCViewsAbtract = class
TMVCViewsAbstract = class
protected
fFactory: TInterfaceFactory;
fLogClass: TSynLogClass;
@ -105,7 +105,8 @@ type
fViewGenerationTimeTag: RawUTF8;
procedure SetViewTemplateFolder(const aFolder: TFileName);
/// overriden implementations should return the rendered content
procedure Render(methodIndex: Integer; const Context: variant; var View: TMVCView); virtual; abstract;
procedure Render(methodIndex: Integer; const Context: variant;
var View: TMVCView); virtual; abstract;
/// return the static file contents - from fViewStaticFolder by default
// - called if cacheStatic has been defined
function GetStaticFile(const aFileName: TFileName): RawByteString; virtual;
@ -148,7 +149,7 @@ type
end;
/// a class able to implement Views using Mustache templates
TMVCViewsMustache = class(TMVCViewsAbtract)
TMVCViewsMustache = class(TMVCViewsAbstract)
protected
fViewTemplateFileTimestampMonitor: cardinal;
fViewPartials: TSynMustachePartials;
@ -400,7 +401,7 @@ type
TMVCApplication = class;
/// abtract MVC rendering execution context
/// abstract MVC rendering execution context
// - you shoud not execute this abstract class, but any of the inherited class
// - one instance inherited from this class would be allocated for each event
// - may return some data (when inheriting from TMVCRendererReturningData), or
@ -511,9 +512,9 @@ type
// an optional simple in-memory cache
TMVCRunWithViews = class(TMVCRun)
protected
fViews: TMVCViewsAbtract;
fViews: TMVCViewsAbstract;
fCacheLocker: IAutoLocker;
fCache: array of record
fCache: array of record // fCache[MethodIndex]
Policy: TMVCRendererCachePolicy;
TimeOutSeconds: cardinal;
RootValue: RawUTF8;
@ -523,7 +524,7 @@ type
public
/// link this runner class to a specified MVC application
constructor Create(aApplication: TMVCApplication;
aViews: TMVCViewsAbtract=nil); reintroduce;
aViews: TMVCViewsAbstract=nil); reintroduce;
/// method called to flush the caching mechanism for a MVC command
procedure NotifyContentChangedForMethod(aMethodIndex: integer); override;
/// defines the caching policy for a given MVC command
@ -537,7 +538,7 @@ type
/// finalize this instance
destructor Destroy; override;
/// read-write access to the associated MVC Views instance
property Views: TMVCViewsAbtract read fViews;
property Views: TMVCViewsAbstract read fViews;
end;
/// the kinds of optional content which may be published
@ -587,7 +588,7 @@ type
// - aPublishOptions could be used to specify integration with the server
constructor Create(aApplication: TMVCApplication;
aRestServer: TSQLRestServer=nil; const aSubURI: RawUTF8='';
aViews: TMVCViewsAbtract=nil; aPublishOptions: TMVCPublishOptions=
aViews: TMVCViewsAbstract=nil; aPublishOptions: TMVCPublishOptions=
[low(TMVCPublishOption)..high(TMVCPublishOption)]); reintroduce;
/// define some content for a static file
// - only used if cacheStatic has been defined
@ -645,7 +646,7 @@ type
// - you should inherit from this class, then implement an interface inheriting
// from IMVCApplication to define the various commands of the application
// - here the Model would be a TSQLRest instance, Views will be defined by
// TMVCViewsAbtract (e.g. TMVCViewsMustache), and the ViewModel/Controller
// TMVCViewsAbstract (e.g. TMVCViewsMustache), and the ViewModel/Controller
// will be implemented with IMVCApplication methods of the inherited class
// - inherits from TInjectableObject, so that you could resolve dependencies
// via services or stubs, following the IoC pattern
@ -726,9 +727,9 @@ const
implementation
{ TMVCViewsAbtract }
{ TMVCViewsAbstract }
constructor TMVCViewsAbtract.Create(aInterface: PTypeInfo; aLogClass: TSynLogClass);
constructor TMVCViewsAbstract.Create(aInterface: PTypeInfo; aLogClass: TSynLogClass);
begin
inherited Create;
fFactory := TInterfaceFactory.Get(aInterface);
@ -739,13 +740,13 @@ begin
fViewGenerationTimeTag := '[[GENERATION_TIME_TAG]]';
end;
procedure TMVCViewsAbtract.SetViewTemplateFolder(const aFolder: TFileName);
procedure TMVCViewsAbstract.SetViewTemplateFolder(const aFolder: TFileName);
begin
fViewTemplateFolder := IncludeTrailingPathDelimiter(aFolder);
fViewStaticFolder := IncludeTrailingPathDelimiter(fViewTemplateFolder+STATIC_URI);
end;
function TMVCViewsAbtract.GetStaticFile(const aFileName: TFileName): RawByteString;
function TMVCViewsAbstract.GetStaticFile(const aFileName: TFileName): RawByteString;
begin
result := StringFromFile(fViewStaticFolder + aFileName);
end;
@ -1490,6 +1491,7 @@ begin
[self,aRestModel,fFactory.InterfaceTypeInfo^.Name,URI]) else
// TServiceCustomAnswer maps TMVCAction in TMVCApplication.RunOnRestServer
ArgsResultIsServiceCustomAnswer := true;
FlushAnyCache;
end;
destructor TMVCApplication.Destroy;
@ -1738,7 +1740,7 @@ end;
{ TMVCRunWithViews }
constructor TMVCRunWithViews.Create(aApplication: TMVCApplication;
aViews: TMVCViewsAbtract);
aViews: TMVCViewsAbstract);
begin
inherited Create(aApplication);
fViews := aViews;
@ -1792,7 +1794,7 @@ end;
constructor TMVCRunOnRestServer.Create(aApplication: TMVCApplication;
aRestServer: TSQLRestServer; const aSubURI: RawUTF8;
aViews: TMVCViewsAbtract; aPublishOptions: TMVCPublishOptions);
aViews: TMVCViewsAbstract; aPublishOptions: TMVCPublishOptions);
var m: integer;
bypass: boolean;
method: RawUTF8;

View File

@ -6,7 +6,7 @@ unit mORMotMidasVCL;
{
This file is part of Synopse mORmot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotMidasVCL;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotMongoDB;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotMongoDB;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -84,7 +84,7 @@ uses
SynMongoDB;
type
/// exeception class raised by this units
/// exception class raised by this units
EORMMongoDBException = class(EORMException);
/// how TSQLRestStorageMongoDB would compute the next ID to be inserted
@ -190,6 +190,12 @@ type
/// create one index for all specific FieldNames at once
function CreateSQLMultiIndex(Table: TSQLRecordClass; const FieldNames: array of RawUTF8;
Unique: boolean; IndexName: RawUTF8=''): boolean; override;
/// search for a field value, according to its SQL content representation
// - return true on success (i.e. if some values have been added to ResultID)
// - store the results into the ResultID dynamic array
// - faster than OneFieldValues method, which creates a temporary JSON content
function SearchField(const FieldName, FieldValue: RawUTF8;
out ResultID: TIDDynArray): boolean; override;
/// drop the whole table content
// - in practice, dropping the whole MongoDB database would be faster
@ -992,6 +998,32 @@ begin
result := true; // we do not have any Virtual Table yet -> always accept
end;
function TSQLRestStorageMongoDB.SearchField(const FieldName, FieldValue: RawUTF8;
out ResultID: TIDDynArray): boolean;
var query: variant;
id: TBSONIterator;
n: integer; // an external count is actually faster
begin
if (fCollection = nil) or (FieldName = '') or (FieldValue = '') then
result := false else
try
// use {%:%} here since FieldValue is already JSON encoded
query := BSONVariant('{%:%}',
[fStoredClassMapping^.InternalToExternal(FieldName), FieldValue], []);
// retrieve the IDs for this query
if id.Init(fCollection.FindBSON(query, BSONVariant(['_id', 1]))) then begin
n := 0;
while id.Next do
AddInt64(TInt64DynArray(ResultID), n, id.Item.DocItemToInteger('_id'));
SetLength(ResultID, n);
result := true;
end else
result := false;
except
result := false;
end;
end;
function TSQLRestStorageMongoDB.GetJSONValues(const Res: TBSONDocument;
const extFieldNames: TRawUTF8DynArray; W: TJSONSerializer): integer;
function itemFind(item: PBSONElement; itemcount,o1ndx: integer;

View File

@ -6,7 +6,7 @@ unit mORMotReport;
(*
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -27,7 +27,7 @@ unit mORMotReport;
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) 2020
Portions created by Arnaud Bouchez for Synopse are Copyright (C) 2022
Arnaud Bouchez. All Rights Reserved.
Contributor(s):
@ -111,7 +111,7 @@ unit mORMotReport;
- full Unicode text process (even before Delphi 2009)
- speed up and various bug fixes to work with Delphi 5 up to XE3
Modifications © 2009-2020 Arnaud Bouchez
Modifications (c) 2009-2022 Arnaud Bouchez
Version 1.4 - February 8, 2010
- whole Synopse SQLite3 database framework released under the GNU Lesser
@ -4932,11 +4932,9 @@ begin
rc := rcPage;
rc.Top := (fCurrentYPos*1440) div LogY;
LastChar := 0;
with TextLenEx do begin
flags := GTL_DEFAULT;
codepage := CP_ACP;
end;
MaxLen := SendMessage(RichEditHandle, EM_GETTEXTLENGTHEX, Integer(@TextLenEx), 0);
TextLenEx.flags := GTL_DEFAULT;
TextLenEx.codepage := CP_ACP;
MaxLen := SendMessage(RichEditHandle, EM_GETTEXTLENGTHEX, PtrInt(@TextLenEx), 0);
chrg.cpMax := -1;
OldMap := SetMapMode(hdc, MM_TEXT);
try
@ -4945,7 +4943,7 @@ begin
chrg.cpMin := LastChar;
hdc := fCanvas.Handle;
hdcTarget := hdc;
LastChar := SendMessage(RichEditHandle, EM_FORMATRANGE, 1, Integer(@Range));
LastChar := SendMessage(RichEditHandle, EM_FORMATRANGE, 1, PtrInt(@Range));
if EndOfPagePositions<>nil then
AddInteger(EndOfPagePositions^,LastChar);
if cardinal(LastChar)>=cardinal(MaxLen) then

View File

@ -6,7 +6,7 @@ unit mORMotSQLite3;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotSQLite3;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -118,7 +118,7 @@ type
// - SQL statements for record retrieval from ID are prepared for speed
TSQLRestServerDB = class(TSQLRestServer)
private
/// internal copy of the SQLite3 database engine
/// access to the associated SQLite3 database engine
fDB: TSQLDataBase;
/// initialized by Create(aModel,aDBFileName)
fOwnedDB: TSQLDataBase;
@ -511,7 +511,8 @@ type
// - returns the created TSQLVirtualTableModule instance (which will be a
// TSQLVirtualTableModuleSQLite3 instance in fact)
// - will raise an exception of failure
function RegisterVirtualTableModule(aModule: TSQLVirtualTableClass; aDatabase: TSQLDataBase): TSQLVirtualTableModule;
function RegisterVirtualTableModule(aModule: TSQLVirtualTableClass;
aDatabase: TSQLDataBase): TSQLVirtualTableModule;
implementation
@ -519,7 +520,8 @@ implementation
{$ifdef SQLVIRTUALLOGS}
uses
mORMotDB;
{$endif}
{$endif SQLVIRTUALLOGS}
{ TSQLTableDB }
@ -2186,7 +2188,7 @@ begin
TSQLRestStorageExternal(Table.Static).ComputeSQL(prepared^);
SQLite3Log.Add.Log(sllDebug,'vt_BestIndex(%) plan=% -> cost=% rows=%',
[sqlite3.VersionNumber,ord(Prepared^.EstimatedCost),pInfo.estimatedCost,pInfo.estimatedRows]);
{$endif}
{$endif SQLVIRTUALLOGS}
finally
if result<>SQLITE_OK then
sqlite3.free_(Prepared); // avoid memory leak on error

View File

@ -6,7 +6,7 @@ unit mORMotSelfTests;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotSelfTests;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -110,7 +110,7 @@ end;
procedure TTestSynopsemORMotFramework.SynopseLibraries;
begin
//AddCase(TTestLowLevelTypes);
//AddCase(TTestProtocols);
//exit;
AddCase([TTestLowLevelCommon,
TTestLowLevelTypes,
@ -137,7 +137,7 @@ type // mORMot.pas unit doesn't compile with Delphi 5 yet
{$else}
procedure TTestSynopsemORMotFramework._mORMot;
begin
//AddCase(TTestExternalDatabase);
//AddCase(TTestBasicClasses);
//exit; // (*
AddCase([TTestFileBased,TTestFileBasedMemoryMap,TTestFileBasedWAL]);
AddCase(TTestMemoryBased);

View File

@ -6,7 +6,7 @@ unit mORMotService;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotService;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -1129,29 +1129,35 @@ begin
result := FArgsList[Idx];
end;
{$ifdef CPUX86}
{.$define X86JUMPER} // this preliminary version is buggy so disabled
// a single service per excecutable is fine enough for our daemons
// also for proper Delphi 10.4 compilation with no hint
{$endif CPUX86}
{$ifdef X86JUMPER}
procedure JumpToService;
asm
pop eax
mov eax, [eax] // retrieve self value
mov eax, [eax] // retrieve TService self value
mov edx, [esp+4]
call TService.CtrlHandle
ret 4
end;
{$endif CPUX86}
{$endif X86JUMPER}
function TService.GetControlHandler: TServiceControlHandler;
{$ifdef CPUX86}
{$ifdef X86JUMPER}
var AfterCallAddr: Pointer;
Offset: Integer;
{$endif}
{$endif X86JUMPER}
begin
Result := fControlHandler;
if not Assigned(Result) then
ServiceLog.Add.Log(sllError,'%.GetControlHandler with fControlHandler=nil: '+
'use TServiceSingle or set a custom ControlHandler',[self]);
{$ifdef CPUX86}
exit;
{$ifdef X86JUMPER}
if not Assigned(Result) then
begin
raise EServiceException.Create('Automated jumper generation is not working: '+
@ -1169,7 +1175,7 @@ begin
end;
Result := Pointer(fJumper);
end;
{$endif CPUX86}
{$endif X86JUMPER}
end;
function TService.GetInstalled: boolean;
@ -1674,7 +1680,9 @@ begin
ExeVersion.Version.DetailedOrVoid], daemon);
start;
while SynDaemonTerminated = 0 do
{$ifdef FPC}fpPause{$else}pause{$endif};
if GetCurrentThreadID = MainThreadID then
CheckSynchronize(100) else
Sleep(100);
finally
if log <> nil then
log.Log(sllNewRun, 'Stop /% from Sig=%', [TXT[dofork], SynDaemonTerminated], daemon);
@ -2000,9 +2008,6 @@ begin
fLogRotateFileCount := 2;
fServiceName := UTF8ToString(ExeVersion.ProgramName);
fServiceDisplayName := fServiceName;
{$ifndef MSWINDOWS}
fLogPath := GetSystemPath(spLog); // /var/log or $home
{$endif MSWINDOWS}
end;
function TSynDaemonSettings.ServiceDescription: string;

View File

@ -9,7 +9,7 @@ interface
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -28,7 +28,7 @@ interface
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotUI;
(*
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotUI;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -222,7 +222,7 @@ type
TSQLTableToGridAlign = (alLeft, alCenter, alRight);
/// a hidden component, used for displaying a TSQLTable in a TDrawGrid
// - just call TSQLTableToGrid.Create(Grid,Table) to initiate the association
// - just call TSQLTableToGrid.Create(Grid,Table) to initiate the association
// - the Table will be released when no longer necessary
// - any former association by TSQLTableToGrid.Create() will be overridden
// - handle unicode, column size, field sort, incremental key lookup, hide ID
@ -720,7 +720,7 @@ begin
end;
end;
function CreateAnIcon (const Name, Description, Path, Parameters,
function CreateAnIcon(const Name, Description, Path, Parameters,
WorkingDir, IconFilename: TFileName; const IconIndex: Integer;
const RunMinimized: Boolean = false): TFileName;
var Dir: TFileName;
@ -2347,7 +2347,7 @@ end;
procedure TUIComponentsPersist.TrackControls(const ctrls: array of TComponent);
var
i: integer;
i: PtrInt;
begin
for i := 0 to high(ctrls) do
ObjArrayAddOnce(fTracked, ctrls[i]);
@ -2359,5 +2359,12 @@ begin
RegisterComponents('Synopse',[TSynLabeledEdit]);
end;
initialization
{$ifdef FPC}
// LCL/Lazarus components expect UTF-8 encoding for strings
CurrentAnsiConvert := UTF8AnsiConvert;
{$endif FPC}
end.

View File

@ -6,7 +6,7 @@ unit mORMotUIEdit;
(*
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotUIEdit;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotUILogin;
(*
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotUILogin;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotUIOptions;
(*
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotUIOptions;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotUIQuery;
(*
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotUIQuery;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotVCL;
{
This file is part of Synopse mORmot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotVCL;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,7 +6,7 @@ unit mORMotWrappers;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMotWrappers;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s) (for this unit and the .mustache templates):

View File

@ -6,7 +6,7 @@ unit mORMoti18n;
(*
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit mORMoti18n;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

Binary file not shown.

View File

@ -6,10 +6,10 @@ unit SpiderMonkey;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - http://synopse.info
SyNode for mORMot Copyright (C) 2020 Pavel Mashlyakovsky & Vadim Orel
SyNode for mORMot Copyright (C) 2022 Pavel Mashlyakovsky & Vadim Orel
pavel.mash at gmail.com
*** BEGIN LICENSE BLOCK *****
@ -26,7 +26,7 @@ unit SpiderMonkey;
The Initial Developer of the Original Code is
Pavel Mashlyakovsky & Vadim Orel.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -1,10 +1,10 @@
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - http://synopse.info
Scripting support for mORMot Copyright (C) 2020 Pavel Mashlyakovsky
Scripting support for mORMot Copyright (C) 2022 Pavel Mashlyakovsky
pavel.mash at gmail.com
*** BEGIN LICENSE BLOCK *****
@ -21,7 +21,7 @@
The Initial Developer of the Original Code is
Pavel Mashlyakovsky.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -6,10 +6,10 @@ unit SyNode;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - http://synopse.info
SyNode for mORMot Copyright (C) 2020 Pavel Mashlyakovsky & Vadim Orel
SyNode for mORMot Copyright (C) 2022 Pavel Mashlyakovsky & Vadim Orel
pavel.mash at gmail.com
Some ideas taken from
@ -30,7 +30,7 @@ unit SyNode;
The Initial Developer of the Original Code is
Pavel Mashlyakovsky.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -383,7 +383,7 @@ type
FEngineClass: TSMEngineClass;
/// List of loaded dll modules
FDllModules: TRawUTF8ListHashedLocked;
FDllModules: TRawUTF8List;
/// Path to core modules
FCoreModulesPath: RawUTF8;
FEngineExpireTimeOutTicks: Int64;
@ -559,7 +559,7 @@ const
);
var
GlobalSyNodeBindingHandlers: TRawUTF8ListHashedLocked;
GlobalSyNodeBindingHandlers: TRawUTF8List;
/// handle errors from JavaScript. Just call DoProcessJSError of corresponding TSMEngine
// to set TSMEngine error properties
@ -1116,7 +1116,7 @@ class procedure TSMEngineManager.RegisterBinding(const Name: RawUTF8;
const handler: TSMProcessBindingHandler);
begin
if GlobalSyNodeBindingHandlers = nil then
GlobalSyNodeBindingHandlers := TRawUTF8ListHashedLocked.Create(false);
GlobalSyNodeBindingHandlers := TRawUTF8List.Create(false);
GlobalSyNodeBindingHandlers.AddObject(Name, TObject(@handler));
end;
@ -1125,7 +1125,7 @@ var
obj: TObject;
handler: TSMProcessBindingHandler absolute obj;
begin
obj := GlobalSyNodeBindingHandlers.GetObjectByName(Name);
obj := GlobalSyNodeBindingHandlers.GetObjectFrom(Name);
result := handler;
end;
@ -1153,7 +1153,7 @@ begin
{$ifdef ISDELPHIXE2}
FRttiCx := TRttiContext.Create();
{$endif}
FDllModules := TRawUTF8ListHashedLocked.Create();
FDllModules := TRawUTF8List.Create();
FCoreModulesPath := aCoreModulesPath;
FWorkersManager := TJSWorkersManager.Create;
end;
@ -1261,7 +1261,7 @@ begin
cx.BeginRequest;
try
dirname := ExtractFilePath(UTF8ToString(filename)) ;
ModuleRec := PDllModuleRec(FDllModules.GetObjectByName(filename));
ModuleRec := PDllModuleRec(FDllModules.GetObjectFrom(filename));
if ModuleRec = nil then begin
fHandle := {$IFDEF FPC}dynlibs.{$ENDIF}SafeLoadLibrary(UTF8ToString(filename));
if fHandle=0 then
@ -1422,7 +1422,7 @@ begin
try
i := ThreadEngineIndex(aThreadID);
if i>=0 then begin
(FEnginePool[i] as TSMEngine).GarbageCollect;
(TObject(FEnginePool[i]) as TSMEngine).GarbageCollect;
FEnginePool.Delete(i);
end;
finally

View File

@ -670,7 +670,7 @@ begin
end;
stream := TFileStream.Create(filePath, fmOpenReadWrite);
stream.Seek(0, soFromEnd);
stream.Seek(0, soEnd);
writer := SynCommons.TTextWriter.Create(stream, 65536);
try
vp.rval := SyNodeReadWrite.SMWrite_impl(cx, argc - 1, @in_argv[1], writer);

View File

@ -5,10 +5,10 @@ unit SyNodeNewProto;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - http://synopse.info
SyNode for mORMot Copyright (C) 2020 Pavel Mashlyakovsky & Vadim Orel
SyNode for mORMot Copyright (C) 2022 Pavel Mashlyakovsky & Vadim Orel
pavel.mash at gmail.com
Some ideas taken from
@ -29,7 +29,7 @@ unit SyNodeNewProto;
The Initial Developer of the Original Code is
Pavel Mashlyakovsky.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):

View File

@ -5,10 +5,10 @@ unit SyNodeProto;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - http://synopse.info
SyNode for mORMot Copyright (C) 2020 Pavel Mashlyakovsky & Vadim Orel
SyNode for mORMot Copyright (C) 2022 Pavel Mashlyakovsky & Vadim Orel
pavel.mash at gmail.com
Some ideas taken from

View File

@ -5,10 +5,10 @@ unit SyNodeRemoteDebugger;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - http://synopse.info
SyNode for mORMot Copyright (C) 2020 Pavel Mashlyakovsky & Vadim Orel
SyNode for mORMot Copyright (C) 2022 Pavel Mashlyakovsky & Vadim Orel
pavel.mash at gmail.com
Some ideas taken from
@ -141,8 +141,8 @@ type
private
fIndex: Integer;
fIsPaused: boolean;
fMessagesQueue: TRawUTF8ListLocked;
fLogQueue: TRawUTF8ListLocked;
fMessagesQueue: TRawUTF8List;
fLogQueue: TRawUTF8List;
{$IFNDEF SM52}
fOldInterruptCallback: JSInterruptCallback;
{$ENDIF}
@ -221,7 +221,7 @@ begin
eng := fManager.EngineForThread(curThreadID);
if eng<>nil then begin
Debugger := eng.PrivateDataForDebugger;
Debugger.fLogQueue.SafePush(Text);
Debugger.fLogQueue.Add(Text);
if eng.cx.IsRunning then
{$IFDEF SM52}
@ -522,7 +522,7 @@ begin
engine := fParent.fManager.EngineForThread(fDebugger.fSmThreadID);
if (engine <> nil) then begin
fDebugger.fMessagesQueue.SafePush(VariantToUTF8(request));
fDebugger.fMessagesQueue.Add(VariantToUTF8(request));
if not fDebugger.fIsPaused then begin
if (not engine.cx.IsRunning) then begin
if not Assigned(engine.doInteruptInOwnThread) then
@ -599,8 +599,8 @@ end;
procedure TSMDebugger.attach(aThread: TSMRemoteDebuggerCommunicationThread);
begin
fCommunicationThread := aThread;
fMessagesQueue.SafeClear;
fLogQueue.SafeClear;
fMessagesQueue.Clear;
fLogQueue.Clear;
end;
constructor TSMDebugger.Create(aParent: TSMRemoteDebuggerThread; aEng: TSMEngine);
@ -617,8 +617,8 @@ begin
fSmThreadID := GetCurrentThreadId;
fMessagesQueue := TRawUTF8ListLocked.Create();
fLogQueue := TRawUTF8ListLocked.Create();
fMessagesQueue := TRawUTF8List.Create();
fLogQueue := TRawUTF8List.Create();
fNameForDebug := aEng.nameForDebug;
fDebuggerName := 'synode_debPort_' + aParent.fPort;
fWebAppRootPath := aEng.webAppRootDir;
@ -680,8 +680,8 @@ var
dbgObject: PJSRootedObject;
res: Boolean;
begin
fMessagesQueue.SafeClear;
fLogQueue.SafeClear;
fMessagesQueue.Clear;
fLogQueue.Clear;
cx := aEng.cx;
cmpDbg := cx.EnterCompartment(aEng.GlobalObjectDbg.ptr);
@ -765,7 +765,7 @@ function debugger_read(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
debugger: TSMDebugger;
msg: RawUTF8;
Queue: TRawUTF8ListLocked;
Queue: TRawUTF8List;
begin
debugger := TSMEngine(cx.PrivateData).PrivateDataForDebugger;
if (argc = 0) or vp.argv[0].asBoolean then
@ -774,7 +774,7 @@ begin
Queue := debugger.fLogQueue;
msg := '';
while ((Queue <> nil) and (debugger.fCommunicationThread <> nil) and
(not Queue.SafePop(msg))) and (argc = 0) do
(not Queue.PopFirst(msg))) and (argc = 0) do
SleepHiRes(10);
result := true;
if (Queue <> nil) and (debugger.fCommunicationThread <> nil) then

View File

@ -5,10 +5,10 @@ unit SyNodeSimpleProto;
{
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - http://synopse.info
SyNode for mORMot Copyright (C) 2020 Pavel Mashlyakovsky & Vadim Orel
SyNode for mORMot Copyright (C) 2022 Pavel Mashlyakovsky & Vadim Orel
pavel.mash at gmail.com
Some ideas taken from
@ -180,7 +180,7 @@ begin
PI.SetInt64Prop(Instance^.instance, val.asInt64);
tkFloat:
PI.SetFloatProp(Instance^.instance, val.asDouble);
tkLString,{$IFDEF FPC}tkLStringOld{$ENDIF},tkWString{$ifdef HASVARUSTRING},tkUString{$endif}:
tkLString{$IFDEF FPC},tkLStringOld{$ENDIF},tkWString{$ifdef HASVARUSTRING},tkUString{$endif}:
PI.SetLongStrValue(Instance^.instance, val.asJsString.ToUTF8(cx));
else
raise ESMException.Create('NotImplemented');

View File

@ -6,7 +6,7 @@ unit SynBidirSock;
{
This file is part of the Synopse framework.
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
@ -25,7 +25,7 @@ unit SynBidirSock;
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2020
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
@ -1291,7 +1291,6 @@ implementation
{ -------------- high-level SynCrtSock classes depending on SynCommons }
{ THttpRequestCached }
constructor THttpRequestCached.Create(const aURI: RawUTF8;
@ -1343,6 +1342,11 @@ begin
headin := headin+fTokenHeader;
end;
if fSocket<>nil then begin
if connectionClose in fSocket.HeaderFlags then begin
// server may close after a few requests (e.g. nginx keepalive_requests)
FreeAndNil(fSocket);
fSocket := THttpClientSocket.Open(fURI.Server,fURI.Port)
end;
status := fSocket.Get(aAddress,fKeepAlive,headin);
result := fSocket.Content;
end else

Some files were not shown because too many files have changed in this diff Show More