You are not logged in.
... there is no need for special mormot version ...
that is true. newpascal-ccr/mORMot was created as temporary solution:
1. to host binary files in repository for supported platforms
2. was created for initial support for Mac.
3. as important part of CI for NewPascal
All is merged now into "upstream" mORMot repository, so even NewPascal CI system uses https://github.com/synopse/mORMot .
no need for NewPascal, mormot trunk works fine with current ...
Rather bad idea. We have many low level details and latest stable FPC is not fully functionally with mORMot. After all NewPascal is practically just special selected FPC trunk version (FPC trunk has Management Operators too). Generally:
NewPascal means = selected Lazarus Trunk + selected FPC trunk (with features preview) + selected mORMot version.
@ab did we have any bugreport on FPC mantis for this?
AOG is right. Usage of pure FPC trunk for mORMot has no sense and is extremely risky. I have special version of mORMot which is *almost* ready and compatible both with current FPC trunk and with next NewPascal release:
https://github.com/maciej-izak/mORMot/tree/fpc_newrtti
The branch of mORMot is incomplete yet (we have some serious bug around - and ready to use by FPC core team related patch):
http://bugs.freepascal.org/view.php?id=31249
http://bugs.freepascal.org/view.php?id=31305
#31249 will be fixed directly in NewPascal (or even in FPC trunk).
At the moment we need to wait - I am working with Sven from FPC core team on some important changes for RTTI (few notes : http://synopse.info/forum/viewtopic.php?id=3689 ). mORMot will be updated too, but it has not much sense at the moment. Generally for mORMot NewPascal is recommended. If for some reasons you like to use "pure FPC trunk" the latest stable and recommended for mORMot is r35095 (pre RTTI.pas). Latest NewPascal (v1.0.39) is synced with r35095.
TLTR: For mORMot users I see 2 options:
* use NewPascal (for latest fresh stuff) instead of FPC trunk otherwise you will have many troubles ![]()
* use latest stable FPC 3.0.x (older FPC stable like 2.6.x means also many troubles)
Sorry but no. The only proper solution is to use {$ifndef VER3_0} (FPC has official support only for latest stable version and for trunk (2.6.x is not officially supported anymore so {$ifndef VER3_0} make sense). Eventually if we like to support somehow 2.6.x we could use {$if FPC_FULLVERSION>30100} instead of {$ifndef VER3_0}.
I know that you are not happy about that but IMO is impossible to put for each change new FPC_HAS_* ...
I do what I can best for improved RTTI. Finally we have small steps in right direction.
I will inform about all breaking changes in this topic. We have again small update in https://github.com/newpascal/freepascal … 437b3288e1
First post updated (InitTable field)!
I mean moving units to directories. We should for example move
PasZip, SynBidirSock, SynBigTable, SynCommons, SynCrtSock, SynCrypto, SynFastWideString, SynFPCTypInfo, SynLog, SynLZ, SynLZO, SynMustache, SynSSPIAuth, SynWinSock, SynZip, SynZipFiles, SynTests
to "Common" directory or something similar.
Note: Zeos is excellent example of good designed packages for Lazarus.
Sorry but no. The only proper solution is to use {$ifndef VER3_0} (FPC has official support only for latest stable version and for trunk (2.6.x is not officially supported anymore so {$ifndef VER3_0} make sense). Eventually if we like to support somehow 2.6.x we could use {$if FPC_FULLVERSION>30100} instead of {$ifndef VER3_0}.
FPC is open source so IMO we (as mORMot community) don't need to provide support for versions older than 3.0.x...
IIRC for packages we need to refresh mORMot source code tree and that is main problem...
Thanks for your work. First real book about mORMot!
Btw. nice to see NewPascal in description ![]()
Use either Delphi Professional or the free and open source New Pascal to develop on Windows, Linux and other platforms. Active Directory authentication and local passwords are both demonstrated.
Hi,
we have very important changes for FPC (comes from NewPascal with a few adjustments):
https://github.com/newpascal/freepascal … 9ffaa8a6ad
RTTI layout for records is different and in few cases more Delphi compatible. Maybe not directly compatible... Now is possible to obtain access to *real* managed fields (by RTTI INIT table for records by InitTable field). New layout:
PRecordInfoFull=^TRecordInfoFull;
TRecordInfoFull={$ifdef USE_PACKED} packed{$endif USE_PACKED}
record
InitTable: Pointer; // PRecordInfoInit
Size: Longint;
Count: Longint;
{ Elements: array[count] of TRecordElement }
end;
PRecordInfoInit=^TRecordInfoInit;
TRecordInfoInit= {$ifdef USE_PACKED}packed{$endif USE_PACKED}
record
Terminator: Pointer;
Size: Longint;
Count: Longint;
{ Elements: array[count] of TRecordElement }
end;This RTTI is incompatible (yet) with NewPascal. I need to merge this in proper way. In NewPascal InitTable/Terminator is placed after Size field.
Anyway more breaking changes is coming... So be aware.
We have initial idea of packages (see packages directory): https://github.com/maciej-izak/mORMot/tree/packages
I think we can add some script *.bat/*.sh file for auto-reorganizing source directory for packages.
Proposed packages:
* Package mORMot_Commons
modules:
PasZip, SynBidirSock, SynBigTable, SynCommons, SynCrtSock, SynCrypto,
SynFastWideString, SynFPCTypInfo, SynLog, SynLZ, SynLZO, SynMustache,
SynSSPIAuth, SynWinSock, SynZip, SynZipFiles, SynTests
required packages:
FCL (standard basic package for packages)
* Package mORMot_SQLite3
modules:
mORMot, mORMotSQLite3, SynSQLite3
required packages:
mORMot_Commons
* Package mORMot_REST
modules:
mORMotHttpClient, mORMotHttpServer, mORMotWrappers,
required packages:
mORMot_SQLite3
* Package mORMot_SQLite3Static
modules:
SynSQLite3Static
required packages:
mORMot_SQLite3
* Package mORMot_DB
modules:
mORMotDB, mORMotMongoDB, SynDB, SynDBODBC, SynDBOracle, SynDBRemote,
SynDBSQLite3, SynMongoDB
required packages:
mORMot_SQLite3
* Package mORMot_ZEOS (needs some work/idea for paths to ZEOS)
modules:
SynDBZeos
required packages:
* zdbc
* mORMot_DB
@ab this code is not used in presented "batch save/load system" (just too much copy/paste from my helper module). Btw. It works perfectly on all of my FPC targets and is very standard for Pascal: ARM, Linux/Windows... without problems.
@edwinsn you have string per operation: Add has own string, Delete has own string, Update has own string. The best idea is to store this "batch" strings in separate files (or in single SQLite file as records):
FileFromString(TBatchCacheFactory.Add(...), 'C:\add001.txt');
FileFromString(TBatchCacheFactory.Update(...), 'C:\update001.txt');
FileFromString(TBatchCacheFactory.Delete(...), 'C:\delete001.txt');
...
myDb.BatchSend(nil, StringFromFile('C:\add001.txt'));
myDb.BatchSend(nil, StringFromFile('C:\update001.txt'));
myDb.BatchSend(nil, StringFromFile('C:\delete001.txt'));you can also try to use somehow single text file
mORMot is super elastic.
I didn't know that it is impossible yet and I have created solution for this.
First you need additional factory to produce batch string:
type
TBatchAction = (baInsert, baUpdate, baDelete);
TBatchCacheFactory = class
public
class function Insert(AServer: TSQLRestServerDB; ARecord: TSQLRecord; AForceID: Boolean = false;
const CustomFields: TSQLFieldBits = []): RawUTF8; overload;
class function Insert(AServer: TSQLRestServerDB; ARecord: TSQLRecord; AForceID: Boolean = false;
const CustomCSVFields: RawUTF8 = '*'): RawUTF8; overload;
class function Update(AServer: TSQLRestServerDB; ARecord: TSQLRecord; const CustomFields: TSQLFieldBits = []): RawUTF8; overload;
class function Update(AServer: TSQLRestServerDB; ARecord: TSQLRecord; const CustomCSVFields: RawUTF8): RawUTF8; overload;
class function Delete(AServer: TSQLRestServerDB; ARecord: TSQLRecordClass; AID: TID): RawUTF8;
end;
implementation
{ TBatchCacheFactory }
class function TBatchCacheFactory.Insert(AServer: TSQLRestServerDB; ARecord: TSQLRecord;
AForceID: Boolean; const CustomFields: TSQLFieldBits): RawUTF8;
var
LBatch: TSQLRestBatch;
begin
with TAutoFree.One(LBatch, TSQLRestBatch.Create(AServer, nil)) do
begin
LBatch.Add(ARecord, true, AForceID, CustomFields);
LBatch.PrepareForSending(Result);
end;
end;
class function TBatchCacheFactory.Update(AServer: TSQLRestServerDB; ARecord: TSQLRecord;
const CustomFields: TSQLFieldBits): RawUTF8;
var
LBatch: TSQLRestBatch;
begin
with TAutoFree.One(LBatch, TSQLRestBatch.Create(AServer, nil)) do
begin
LBatch.Update(ARecord, CustomFields);
LBatch.PrepareForSending(Result);
end;
end;
class function TBatchCacheFactory.Delete(AServer: TSQLRestServerDB; ARecord: TSQLRecordClass;
AID: TID): RawUTF8;
var
LBatch: TSQLRestBatch;
begin
with TAutoFree.One(LBatch, TSQLRestBatch.Create(AServer, nil)) do
begin
LBatch.Delete(ARecord, AID);
LBatch.PrepareForSending(Result);
end;
end;
class function TBatchCacheFactory.Insert(AServer: TSQLRestServerDB;
ARecord: TSQLRecord; AForceID: Boolean;
const CustomCSVFields: RawUTF8): RawUTF8;
begin
Result := Insert(AServer, ARecord, AForceID, ARecord.RecordProps.FieldBitsFromCSV(CustomCSVFields));
end;
class function TBatchCacheFactory.Update(AServer: TSQLRestServerDB; ARecord: TSQLRecord;
const CustomCSVFields: RawUTF8): RawUTF8;
begin
Result := Update(AServer, ARecord, ARecord.RecordProps.FieldBitsFromCSV(CustomCSVFields));
end;next you need some helpers for existing mORMot classes:
type
TSQLRestBatchHelper = class helper for TSQLRestBatch
public
function Add(Value: TSQLRecord; SendData: boolean; ForceRecordClass: TSQLRecordClass; ForceID: boolean=false;
const CustomFields: TSQLFieldBits=[]; DoNotAutoComputeFields: boolean=false): integer; overload;
end;
{ TSQLRestHelper }
TSQLRestHelper = class helper for TSQLRest
public
function BatchSend(Batch: TSQLRestBatch; const Data: RawUTF8; var Results: TIDDynArray): integer; overload;
function BatchSend(Batch: TSQLRestBatch; const Data: RawUTF8): integer; overload;
function Update(Value: TSQLRecord; ForceRecordClass: TSQLRecordClass;
const CustomFields: TSQLFieldBits=[]; DoNotAutoComputeFields: boolean=false): boolean; overload;
end;
implementation
{ TSQLRestHelper }
function TSQLRestHelper.BatchSend(Batch: TSQLRestBatch; const Data: RawUTF8;
var Results: TIDDynArray): integer;
var
LData: RawUTF8;
begin
LData := Copy(Data, 1);
try
if Batch <> nil then
result := EngineBatchSend(Batch.Table,LData,Results,Batch.Count)
else
result := EngineBatchSend(nil,LData,Results,0)
except
on Exception do // e.g. from TSQLRestServer.EngineBatchSend()
result := HTTP_SERVERERROR;
end;
end;
function TSQLRestHelper.BatchSend(Batch: TSQLRestBatch; const Data: RawUTF8
): integer;
var
Results: TIDDynArray;
begin
Result := BatchSend(Batch,Data,Results);
end;
function TSQLRestHelper.Update(Value: TSQLRecord;
ForceRecordClass: TSQLRecordClass; const CustomFields: TSQLFieldBits;
DoNotAutoComputeFields: boolean): boolean;
var
DefaultClass: TSQLRecordClass;
begin
Result := False;
if (self=nil) or (Value=nil) or (ForceRecordClass=nil) then
exit;
if not PSQLRecordClass(Value)^.InheritsFrom(ForceRecordClass) then
exit;
DefaultClass := PSQLRecordClass(Value)^;
PSQLRecordClass(Value)^ := ForceRecordClass;
result := Self.Update(Value, CustomFields, DoNotAutoComputeFields);
PSQLRecordClass(Value)^ := DefaultClass;
end;
{ TSQLRestBatchHelper }
function TSQLRestBatchHelper.Add(Value: TSQLRecord; SendData: boolean;
ForceRecordClass: TSQLRecordClass; ForceID: boolean;
const CustomFields: TSQLFieldBits; DoNotAutoComputeFields: boolean): integer;
var
DefaultClass: TSQLRecordClass;
begin
result := -1;
if (self=nil) or (Value=nil) or (fBatch=nil) or (ForceRecordClass=nil) then
exit;
if not PSQLRecordClass(Value)^.InheritsFrom(ForceRecordClass) then
exit;
DefaultClass := PSQLRecordClass(Value)^;
PSQLRecordClass(Value)^ := ForceRecordClass;
result := Self.Add(Value, SendData, ForceID, CustomFields, DoNotAutoComputeFields);
PSQLRecordClass(Value)^ := DefaultClass;
end;now put all together:
LBatch := TBatchCacheFactory.Update(AServer, LMyRecord, LCSV); // save batch to string
{ ... }
{ save batch to file or anywhere }
{ ... }
FServer.BatchSend(nil, LBatch); // execute our stringdone! ^^
I will work on OAuth2 this weekend.
Thanks for sharing.
@ab any progress ? Seems like OAuth2 work takes more than one weekend ;)
It seems that the memory footer corruption can be avoided with some modification of TSynTempBuffer to avoid the re-allocation in TSynTempBuffer.Init. The reason is unclear. Could ab or you help to correct the root problem ?
Maybe related to http://synopse.info/forum/viewtopic.php?id=3468 ?
@ab: TSynUniqueIdentifierGenerator might be good for new projects but this solution is not perfect (for example is impossible to use TSynUniqueIdentifierGenerator for large existing systems).
@oz: I have 1 Master, N Slaves for described scenario (but in background I have much more complicated architecture with N Masters but that is managed outside mORMot by other tools). The idea for "1 Master - N Slaves" is simple and can be improved by ORM system. What we have:
* android clients (with local SQLite3 DB)
* 1 server mORMot server
* 1 PostgreSQL DB
* Existing big project developed for more than 10 years with 500+ tables (so I can't start from scratch - structures like TSynUniqueIdentifierGenerator are useless for me).
The first step is model:
TSQLmaster = class(TSQLRecord)
// ...
end;
TCACHE_master = class(TSQLmaster) // could be generated by ORM
protected
fiid: RawUTF8;
fcreation_time: TDateTime;
published
property iid: RawUTF8 index 38 read fiid write fiid;
property creation_time: TDateTime read fcreation_time write fcreation_time;
end;
TSQLdetail = class(TSQLRecord)
// ...
end;
TCACHE_detail = class(TSQLdetail) // could be generated by ORM
protected
fiid: RawUTF8;
fcreation_time: TDateTime;
published
property iid: RawUTF8 index 38 read fiid write fiid;
property creation_time: TDateTime read fcreation_time write fcreation_time;
end;
TCACHE_CACHE_detail = class(TCACHE_detail); // could be generated by ORMIn presented model we have 5 tables: master, CACHE_master, detail, CACHE_detail, CACHE_CACHE_detail
How it is handled by client? For the client, insertion for new records into master/detail table is forbidden. We have simple schema:
* insert for new detail record for correlated master record means hidden insertion into CACHE_detail table
* insert for new master record means hidden insertion into CACHE_master table
* insert for new detail record for correlated CACHE_master record means hidden insertion into CACHE_CACHE_detail table
Each insert above means also assigning for iid and creation_time:
newrecord.iid := GUIDToRawUTF8(RandomGUID);
newrecord.creation_time := DBServerDateTime; thanks this solution I can receive new data from server without problem and I don't need to send records from CACHE_* tables in the same time (but that scenario is also possible
.
When client like to send data (or few parts) I have special record for that purpose:
TSYNC_CACHE = class(TSQLRecord)
protected
fiid: RawUTF8;
fcreation_time: TDateTime;
{ ...
structures (for example dynamic arrays) to handle CACHE_* tables and batching
}
published
property iid: RawUTF8 index 38 read fiid write fiid;
property creation_time: TDateTime read fcreation_time write fcreation_time;
{ ...
structures (for example dynamic arrays) to handle CACHE_* tables and batching }
}
end;on server side I have local SQLite3 file DB to track all iid which is the key for proper working, in some cases mORMot can send twice (or even more) the same data, or even corrupted data but this is different topic. Anyway TSYNC_CACHE and each item inside needs iid. When server has done all insertions described in TSYNC_CACHE (with insertions into iid table) then server sends back TSYNC_CACHE to client but with new correct ID and optionally with new values for some columns for each record inside TSYNC_CACHE.
Also very important for proper ID is right configuration for regular tables on Server side:
procedure AddEngineComputeId(AClass: TSQLRecordClass);
begin
LRestStorage := TSQLRestStorageExternal.Instance(AClass, AServer);
LRestStorage.EngineAddForcedID := 0;
LRestStorage.OnEngineAddComputeID := aRestServer.OnEngineAddComputeID; // here inside OnEngineAddComputeID is called nextval for postgreSQL sequence
end;
procedure AddEngineIgnoreId(AClass: TSQLRecordClass); // for simple insert
begin
LRestStorage := TSQLRestStorageExternal.Instance(AClass, AServer);
LRestStorage.EngineAddForcedID := 1;
end;thanks to mORMot and metaclasses, manipulations between CACHE_* and original tables on client/server side is easy. All described above can be handled on ORM level in very elastic way with many policies. The topic is complex and is very hard to decribe many aspects in one post. Thanks to CACHE_* tables, batching and iid table (and mORMot of course) I have done full CRUD + master/slave replication + online/offline modes ![]()
Comment from ab: http://synopse.info/forum/viewtopic.php … 877#p17877
so my "two way" master/slave replication with offline functionality is developed outside mORMot sources
. Anyway code attached above is the key to start any serious work ![]()
@AOG:
GClientPing is a global variable and is created together with regular clients (at application startup). I need that because TSQLHttpClientWinSock is not thread safe. I have few threads in application:
1. GUI
2. Data sync
3. Ping
About security: I have private WiFi network in each place where my application is used. Additionally is used standard hcSynShaAes for data compression.
About functionality, look at new topic:
I'd like to start new topic, maybe it will help someone. The discussion starts here:
http://synopse.info/forum/viewtopic.php … 890#p21890
My application has almost full functionality when is offline (except printing
- for that server is needed). I made my own replication master/slave which is fully functional in offline mode (each slave can add or modify records even in offline). Each table (lets call normal table "main table") on client side has one or more special "cache" tables (each of "cache" table has exactly the same columns - technically each "cache" table is descendant of main table). I have my own extensions for mORMot for that. On the server side each cache table is simple converted into original table (and each of record from "cache" table is converted and sent back as record for "main table" to client, the process is monitored/protected so even when I lose connection at critical moment of sync all is tracked in proper way thanks to unique GUID for each action). The core code for improved master/slave replication (rejected by ab
):
uses
mORMot, SynCommons, SysUtils;
type
TSQLRestBatchHelper = class helper for TSQLRestBatch
public
function Add(Value: TSQLRecord; SendData: boolean; ForceRecordClass: TSQLRecordClass; ForceID: boolean=false;
const CustomFields: TSQLFieldBits=[]; DoNotAutoComputeFields: boolean=false): integer; overload;
end;
{ TSQLRestHelper }
TSQLRestHelper = class helper for TSQLRest
public
function BatchSend(Batch: TSQLRestBatch; const Data: RawUTF8; var Results: TIDDynArray): integer; overload;
function BatchSend(Batch: TSQLRestBatch; const Data: RawUTF8): integer; overload;
function Update(Value: TSQLRecord; ForceRecordClass: TSQLRecordClass;
const CustomFields: TSQLFieldBits=[]; DoNotAutoComputeFields: boolean=false): boolean; overload;
end;
implementation
{ TSQLRestHelper }
function TSQLRestHelper.BatchSend(Batch: TSQLRestBatch; const Data: RawUTF8;
var Results: TIDDynArray): integer;
var
LData: RawUTF8;
begin
LData := Copy(Data, 1);
try
if Batch <> nil then
result := EngineBatchSend(Batch.Table,LData,Results,Batch.Count)
else
result := EngineBatchSend(nil,LData,Results,0)
except
on Exception do // e.g. from TSQLRestServer.EngineBatchSend()
result := HTTP_SERVERERROR;
end;
end;
function TSQLRestHelper.BatchSend(Batch: TSQLRestBatch; const Data: RawUTF8
): integer;
var
Results: TIDDynArray;
begin
Result := BatchSend(Batch,Data,Results);
end;
function TSQLRestHelper.Update(Value: TSQLRecord;
ForceRecordClass: TSQLRecordClass; const CustomFields: TSQLFieldBits;
DoNotAutoComputeFields: boolean): boolean;
var
DefaultClass: TSQLRecordClass;
begin
Result := False;
if (self=nil) or (Value=nil) or (ForceRecordClass=nil) then
exit;
if not PSQLRecordClass(Value)^.InheritsFrom(ForceRecordClass) then
exit;
DefaultClass := PSQLRecordClass(Value)^;
PSQLRecordClass(Value)^ := ForceRecordClass;
result := Self.Update(Value, CustomFields, DoNotAutoComputeFields);
PSQLRecordClass(Value)^ := DefaultClass;
end;
{ TSQLRestBatchHelper }
function TSQLRestBatchHelper.Add(Value: TSQLRecord; SendData: boolean;
ForceRecordClass: TSQLRecordClass; ForceID: boolean;
const CustomFields: TSQLFieldBits; DoNotAutoComputeFields: boolean): integer;
var
DefaultClass: TSQLRecordClass;
begin
result := -1;
if (self=nil) or (Value=nil) or (fBatch=nil) or (ForceRecordClass=nil) then
exit;
if not PSQLRecordClass(Value)^.InheritsFrom(ForceRecordClass) then
exit;
DefaultClass := PSQLRecordClass(Value)^;
PSQLRecordClass(Value)^ := ForceRecordClass;
result := Self.Add(Value, SendData, ForceID, CustomFields, DoNotAutoComputeFields);
PSQLRecordClass(Value)^ := DefaultClass;
end;The most important parameter is "ForceRecordClass" (to convert cache table into main table). With a little of work my mechanism can be automated for any ORM master/slave replication. Behind scenes is extensively used batching. I have also additional batch parser to extract records from batch like "PUT@table_name,{...}". The topic is complicated but the end effect is amazing. The client/slave can be offline even for very long time (few days?) and all is synchronized very well, thanks to "cache" tables idea. In addition should be possible to manage politics for conflicts (for now I have one rule: "first client wins").
The key code to start any work is presented above. ![]()
@AOG
From my experience the best values for release for production (in WiFi network) are:
const
DEFAULT_CONNECT_TIMEOUT = 3000;
...
AClient := TSQLHttpClientWinSock.Create(AIP, APort, GModel,
SynCrtSock.HTTP_DEFAULT_SENDTIMEOUT, SynCrtSock.HTTP_DEFAULT_RECEIVETIMEOUT, DEFAULT_CONNECT_TIMEOUT);In addition if connection is successful I have dedicated thread (and client GClientPing - created with the same parameters as presented above) for ping purposes (online/offline status):
const
PING_MS_DELTA = DEFAULT_CONNECT_TIMEOUT;
...
class function TSyncProcess.TSync.Ping: Boolean;
var
LNow, LThen: TDateTime;
LTryPing: Boolean;
begin
LNow := TimeLogToDateTime(GClientPing.ServerTimeStamp);
LThen := TimeLogToDateTime(FLastPingTimeStamp);
LTryPing := (FLastPingTimeStamp = 0) or (MilliSecondsBetween(LNow, LThen) > PING_MS_DELTA);
if LTryPing or FLastPingResult then
begin
try
Result := GClientPing.ServerTimeStampSynchronize;
except
Result := False;
end;
Result := Sync.StatusCodeIsSuccess(GClientPing) and Result;
FLastPingResult := Result;
FLastPingTimeStamp := GClientPing.ServerTimeStamp;
end
else
Result := FLastPingResult;
end; NewPascal is developed to put all together. AOG has done new pre-release under NewPascal project, to omit confusion. But we need to finish few details:
https://github.com/dathox/newpascalpack … ss-v1.0.22
NewPascal is Windows only (for now but it might change in future) with cross compilation for many platforms (on the way - see newpascalcross-v1.0.22).
I need to finish series of patches for FPC compiler to keep NewPascal as close as possible with "official" FPC trunk and for documentation purposes. Cooperation with FPC core team is hard, and they are only happy with simple minor patches like:
http://bugs.freepascal.org/view.php?id=30494
FPC core team is not happy with major patches:
http://bugs.freepascal.org/view.php?id=30534
Whatever FPC core team decide (maybe they wish to not including few of patches), we need to put all reports from bugtracker together for documentation purposes. Project like NewPascal needs to have clear and transparent way to document every of change.
Approach of FPC core team is depressing for me...
I am working on new tool for auto-downloading latest mORMot for NewPascal (and for downloading latest NewPascal). When tool will be ready, then I will separate mORMot from NewPascal release.
... it is about to be merged in the official FPC trunk AFAIR.
It may takes months or years.
Do you think of mORMot will follow? Maybe it's too earlier to talk about any decisions this at the moment, but this news is still worth discussing
Waste of time. Delphi has "mORMot incompatible" EULA which makes mORMot usage (development?) illegal in Delphi. Delphi Linux compiler will be ARC only which is big problem.
Amazing! My inner version of NewPascal uses NodeJS for few purposes, but thanks to your work I think is worth to review my code and support this effort ![]()
- fpc support? linux support? x64 support?
FPC is the most important part. The Delphi licence (after XE2) is evil:
Licensee agrees not to use the Product to develop an application that is directly competitive to the Product or to any other Embarcadero products
Seems like mORMot breaks that point in many fields, IMO very bad licence for mORMot... The comment from Embarcadero employee is for me very strange and nasty:
There isn't really any official clarification on what that means. The license agreement is the official clarification - anything "official" beyond that would limit the scope of the agreement.
My understanding is similar to what +Roland Kossow said. Anything that someone would do to specifically target or clone our products. I expect that unless what you are doing is pretty blatant there won't be a reaction.
Basically, don't try to bite the hand that feeds you.
Another point for support for FPC: Delphi Linux compiler will be ARC only.
For extended RTTI for FPC we need to push forward NewPascal project.
Hi,
what do you think? TSynDictionary might be good to improve performance of TSynMustache.HelperFind.
@mpv presented solution will not work with TNullableFloat. For TNullableFloat better is:
NULL -> null
NaN -> -0.0
Infinity -> 1.7976931348623157e+308 (int64 with first byte 0 and all other 1)
-Infinity -> 5e-324But maybe someone have better solution for NaN ?
Yes !
Could you imagine ... the power of mORMot with Delphi starter ... all for free !!
Unfortunately ... just limited in time ... at least the Delphi part.
I think that is the main reason why it is limited in time ![]()
Maybe for NaN: -0.0e-0 or -0.000e-0
for NaN string 'NaN' (or other string) might be better (to omit conflict with TNullableFloat usage).
According to JSON spec, there is no Infinity or NaN values: http://json.org/
Few solutions from Javascript engines: https://lavag.org/topic/16217-cr-json-l … ment-99058
Hi,
we have broken implementation of handling ftDouble field, line 1266 in SynDBZeos:
ftDouble:
if fDBMS in [dMySQL,dPostgreSQL] then begin
P := fResultSet.GetPAnsiChar(col+FirstDbcIndex,Len); In addition to ordinary numeric values, the floating-point types have several special values, P can point to following values/strings:
Infinity
-Infinity
NaN
(source: https://www.postgresql.org/docs/current … meric.html )
for example presented SQL for PostgreSQL won't work with mORMot (that code can corrupt whole ORM data set!):
select CAST('NaN' AS float) as xHow can we handle NaN, Infinity, -Infinity in mORMot? NaN, Infinity, -Infinity are very important for my integration with mORMot for large existing code base. NaN, Infinity, -Infinity is non standard for JSON format but I think we can handle this is similar way like for BASE64 by introducing something similar to JSON_BASE64_MAGIC_QUOTE:
JSON_NAN_MAGIC
JSON_INFINITY_MAGIC
JSON_NEGINFINITY_MAGIC
Hi,
for published field (of dynamic array type) for each array item (each item is TSQLRecord descendant) ignoring of unknown properties won't work. Very dangerous and IMO unexpected behavior.
Is that by design?
Hi,
I think we have bug for TSynTempBuffer.Init(SourceLen: integer); in newer versions of Delphi for TSynTempBuffer (declared as record).
The field tmp is not initialized so on the stack we lose trailing #0 (same for buf, the newly allocated memory is not initialized in any way, and may contain garbage data).
Is that by design?
may I add a feature request ?
a threadsafe list ?
;-)
That one feature is not on my TODO list (yet). Contribution is welcome! Finally it is Open Source ![]()
I am a little surprised that Generics.Collections is part of mORMot "REST-tester" demo. Maybe is worth to move generics-collections in better place in mORMot tree? Keeping that library inside demo is IMO bad idea. Since I have commit rights to fossil I can update Generics.Collections if needed.
Hi,
finally! New release with mORMot and Generics.Collections included is available (at http://newpascal.org ). All works out of box. Just unpack and become mORMot programmer. NP = NewPascal = NoProblems (almost... see below)
Note 0: no cross compilation (yet), only avalible target is Win32
Note 1: IMO we need to refresh mORMot source code tree. Maybe only locally for NewPascal purposes... I know you don't like this ab ![]()
Note 2: Code insight (Code Completion etc) won't work (for example when mORMot module is used). It is +/- Lazarus bug, but well organized source tree can easily eliminate this bug (good example is Zeos).
Looks much better -,-
Any feedback ?
Can I commit my changes to fossil?
Ok.
I have created a NewPascal release that can also be used for cross-compiling.
Can we include that into NewPascalPack Continous Integration system? Btw. you have full admin rights for newpascal repo inside dathox.
Ping. Attached patch has no performance issue for existing code base... That feature has critical meaning for my projects.
Corrected patch: https://drive.google.com/file/d/0B4PZhd … sp=sharing
b) Supports enhanced rtti needed by the mORMot framework, if I understand it correctly.
Ready thanks to patch from AOG
newpascalpack 1.0.3:
http://newpasca.org
https://github.com/dathox/newpascalpack/releases
Hi!
I have idea for custom RTTI PropInfo for custom types. Patch attached:
(moved below)
Example usage:
type
TDouble_Precision2 = type Double;
TSQLPropInfoRTTIDouble_Precision2 = class(TSQLPropInfoRTTIDouble)
public
procedure GetValueVar(Instance: TObject; ToSQL: boolean;
var result: RawUTF8; wasSQLString: PBoolean); override;
end;
implementation
procedure TSQLPropInfoRTTIDouble_Precision2.GetValueVar(Instance: TObject; ToSQL: boolean;
var result: RawUTF8; wasSQLString: PBoolean);
var
D: Double;
begin
if wasSQLString<>nil then
wasSQLString^ := false;
Result := FormatFloat('0.00', PropInfo.GetExtendedValue(Instance));
end;
begin
RegisterSQLPropInfo(TypeInfo(TDouble_Precision2), TSQLPropInfoRTTIDouble_Precision2);
end.I'd like to present the preview of new initiative (and maybe foundation) to support mORMot and other mostly used open source Pascal projects/libraries with recent FPC trunk version (tested in production) tuned for mORMot purposes
. It works even on pendrive - just unpack in any location and have fun. mORMot integration is in progress. All is based on github infrastructure. Any suggestions/propositions are welcome!
I'd like to see mORMot not as core of SupraEngine for critical "gamedev data" but as optional usage to handle: game saving, network, replays, more DB oriented games like tycoon/management games... I think we can somehow adjust mORMot for more game oriented usage. I think there are a lot of possibilities. I can't imagine gamedev without mORMot - well tested on many fields.
My current "lobby goal" is to convince BaRo for using mORMot for presented Unity-style engine
just amazing - game structures and fluent usage of DB backends...
We may use this policy: let mORMot work directly with FPC trunk and latest FPC stable branch (e.g. upcoming 3.0.2).
We can get example from FPC RTL:
FPC RTL must be compileable with the latest release and with trunk. Make cycle compiles 3 times compiler. First pass will compile the RTL
with latest stable release.
To get ride of breaking changes between stable release and trunk version is used:
{$if FPC_FULLVERSION>30100} or {$IFNDEF VER3_0} FPC_FULLVERSION is IMO more PRO
. I see 3 scenarios to handle:
1. FPC_FULLVERSION>30100 to detect FPC trunk
2. FPC_FULLVERSION<30100 to detect latest release
3. FPC_NEWPASCAL or NPC to detect our tuned fork.
maybe I will be able to run complete auto-build system for our fork in this weekend.
The change for dynamic arrays is right move. The topic is quiet old. See my post (in 2013 my post was +/- ignored...):
http://lists.freepascal.org/fpc-devel/2 … 32586.html
You can try to track how it is done in PascalScript (with my patch for this breaking change):
http://forum.lazarus.freepascal.org/ind … #msg196402
http://bugs.freepascal.org/view.php?id=29230
Hi,
during my discussion with Benjamin Rosseaux (aka BeRo) he raised important thing about "\u" escape in JSON:
The most JSON parsers (incl. SynCrossPlatformJSON, as it seems) does it wrong, these have often a incorrect handling of \uXXXX escape sequences: They are pushing even at a surrogate pair of two UTF16 codeunits these parsed UTF16 codeunits simply directly as raw UTF8 sequences (i.e. without first converting this surrogate two UTF16 codeunits to a full unicode codepoint and then from that to the destination encoding, for example UTF8), so that the result is often only CESU8 (or Java's Modified UTF-8) but not valid UTF8 in these cases, see https://en.wikipedia.org/wiki/CESU-8 .
anyway our core GetJSONField in SynCommons.pas for mORMot looks proper. Is some special reason why the SynCrossPlatformJSON implements only CESU8/"Java's Modified UTF-8" escape \u sequence?
@ab, @miab3
thanks for the answers. It was problem on my machine + probably old trunk ZEOS 7.2, anyway now all works as expected.