You are not logged in.
It turns out that if I put the SetExceptionMask line
// https://forum.lazarus.freepascal.org/index.php?topic=70135.15
SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow,exPrecision]);
try
JS_Init();
cx := JSContext.CreateNew(8 * 1024 * 1024);
...
at the very beginning of "HelloSpiderMonkey52.dpr", the sample works fine for FPC & win32.
Nevertheless, it is still confusing for me because the line
TSynFPUException.ForLibraryCode
is already called at the very beginning of `JSContext.CreateNew`.
SyNode/SpiderMonkey.pas:4190:class function JSContext.CreateNew(maxbytes: uint32; maxNurseryBytes: uint32; parentContext: PJSContext): PJSContext;
SyNode/SpiderMonkey.pas-4191-begin
SyNode/SpiderMonkey.pas-4192- with TSynFPUException.ForLibraryCode do begin
SyNode/SpiderMonkey.pas-4193- Result := JS_NewContext(maxbytes, maxNurseryBytes, parentContext);
SyNode/SpiderMonkey.pas-4194- InitSelfHostedCode(Result);
SyNode/SpiderMonkey.pas-4195- end;
Could you help to suggest why would there be "Invalid floating point operation" at "JSContext.CreateNew" when "TSynFPUException.ForLibraryCode" is already called at "JSContext.CreateNew" ? Many thanks !
I remove DebuggerInit from a demo - it's not required for demonstration purpose. The real life example can be found here but it's too complex for a demo app.
The link to download DebuggerInit.js seems to be broken - it leads to login page.
Could you help to update how to obtain and use DebuggerInit.js to debug js code in production ?
Many thanks !
For the time being, I have to put the following code to workaround this problem.
I also have to put in a property to write to Hasher.HashElement...
FMethodsDA.Init(TypeInfo(TSMMethodDynArray), FMethods);
// https://synopse.info/forum/viewtopic.php?id=7313
{$IFNDEF MSWINDOWS}
if @FMethodsDA.Hasher.DHashElement = nil then begin
FMethodsDA.Hasher.DHashElement := DYNARRAY_HASHFIRSTFIELD[False, djSynUnicode];
end;
{$ENDIF}
InitObject(aParent);
It seems that TDynArray.GuessKnownType behaves differently between Delphi Win32 and FPC Linux x86_64.
FPC is installed using the fpcupdeluxe tool and the latest 3.2 fixes branches.
As shown by the figure below, TDynArrayHashed.Init is called in SyNodeProto.TSMCustomProtoObject.Create.
As shown by the two figures below, TDynArrayHashed.Init goes through different routes between Delphi (win32) and FPC Linux x86_64 by design.
In the initialization of the associated hasher, i.e., TDynArrayHasher.Init, HashElement should be initialized to one of default functions. The HashElement under this circumstance is initialized to the function that handle djSynUnicode in Delphi, but nil in FPC Linux x86_64.
It seems that, under FPC Linux x86_64, the reason why the HashElement under this circumstance has been initialized to nil is related to GuessKnownType or related to RTTI. An nil HashElement will cause subsequent ReHash to fail...
Could you help to check and suggest how to fix ? Many thanks !
The SyNode sample "03-HelloSpiderMonkey52-cross" works for both `Delphi & win32` and `FPC & Linux64`.
However, the same sample does not work for `FPC & win32` (compiled on windows, or cross-compiled from linux).
The same synsm.dll used for `Delphi & win32` is used.
The error is access violation at `InitSelfHostedCode`.
For some compiling occasions, the error is `Invalid floating point operation` instead.
As shown by the figures, the operations for `Delphi & win32` and `FPC & win32` seem to be identical.
I could not figure out the cause or the workaround.
Could you help to suggest what could be the cause and how to fix ? Many thanks !
I can resolve an error "Class methods in record types must be static" - it's not a problem.
Problem is about E1030 Invalid compiler directive: 'external'
SpiderMonkey itself is a C++, so we need to export a C++ function as a cdecl.
To avoid unnecessary function call we export a variable what contains pointer to a C++ function from our wrapper - see synsm.h line 10 for example as such:extern "C" { JS_PUBLIC_API(bool) (*SM_Initialize)(void) = &JS_Init;
SM_Initialize is a pointer to a function.
And when import it in the SpiderMonkey.pas using
type TJS_Initialize = function : Boolean; cdecl; var JS_Init: TJS_Initialize external SpiderMonkeyLib name 'SM_Initialize';
I don't know how to do such in Delphi.
It seems that using an explicit SafeLoadLibrary followed by GetProcAddress, one can compile SpiderMonkey.pas in Delphi.
......
SM52Lib := SafeLoadLibrary(SpiderMonkeyLib);
Result := SM52Lib <> INVALID_MODULEHANDLE_VALUE;
if Result then
begin
JS_Init := TJS_Initialize(GetProcAddress(SM52Lib, 'SM_Initialize')^);
JS_ShutDown := TJS_ShutDown(GetProcAddress(SM52Lib, 'SM_ShutDown')^);
.....
The original SpiderMonkey.pas is https://snips.sh/f/C8cKlLp-nw, the modified SpiderMonkey.pas using an explicit SafeLoadLibrary followed by GetProcAddress is https://snips.sh/f/5MQRzLzUmg.
I made a simple test and it seems to work fine.
Could you help to review and test and consider to incorporate ? : D Many thanks !
mpv wrote:...
To avoid unnecessary function call we export a variable what contains pointer to a C++ function from our wrapper - see synsm.h line 10 for example as such:extern "C" { JS_PUBLIC_API(bool) (*SM_Initialize)(void) = &JS_Init;
SM_Initialize is a pointer to a function.
...Could you help to suggest how to build your wrapper SynSM.sln ?
When I open the solution in Visual Studio 2015, there are many errors that are related to includes. For example, "jspubtd.h" cannot resolve "#include "mozilla/Assertions.h"". In the file system, "Assertions.h" is under "mozilla-esr52\mfbt\Assertions.h" not "mozilla"... This seems to be a rather basic problem, since the instructions to build SpiderMonkey itself works, but I am not familiar with C++ or SpiderMonkey. Could you suggest what one needs to do to proceed ? Many thanks !
It seems that one can build SynSM.sln under windows following your how-to : D
One just needs to pay attention to some minor issues.
For example, when one follows the build SM52 how-to, put --disable-shared-js to the configure command line.
The js_static.lib is required for SynSM.sln.
Furthermore, during the mozbuild process, the include files required by SynSM.sln will be put under the i686-mingw/dist.
The last time I compiled this was in 2020. I'll be at my workstation at the end of July, so I can take a look. Unfortunately, I couldn't do it before
It is so great to hear from you !
That will be more than good enough ! Many thanks !
...
To avoid unnecessary function call we export a variable what contains pointer to a C++ function from our wrapper - see synsm.h line 10 for example as such:extern "C" { JS_PUBLIC_API(bool) (*SM_Initialize)(void) = &JS_Init;
SM_Initialize is a pointer to a function.
...
Could you help to suggest how to build your wrapper SynSM.sln ?
When I open the solution in Visual Studio 2015, there are many errors that are related to includes. For example, "jspubtd.h" cannot resolve "#include "mozilla/Assertions.h"". In the file system, "Assertions.h" is under "mozilla-esr52\mfbt\Assertions.h" not "mozilla"... This seems to be a rather basic problem, since the instructions to build SpiderMonkey itself works, but I am not familiar with C++ or SpiderMonkey. Could you suggest what one needs to do to proceed ? Many thanks !
It seems `https://github.com/synopse/mORMot/tree/feature/synodeCleanup` is not quite in sync with master... Could you help to provide documentation or example to help to start using SyNode with mORMot2, preferably with Delphi and with FPC ?
It seems that TSQLHttpClient descendant will try to retry a service call after certain amount of time has lapsed. Furthermore, the client exists with an error code of 505 after another amount of time. However, there seems to be inconsistency between the receive timeout parameter when HttpClient is created and the timing of the second service call and client exit. Therefore, it seems to be confusing concerning setting up the receive timeout parameter.
The compilable programs can be viewed at viewed at gist. The server program is a simple shell wrapper. The client program tries to run a long-running shell command " date ; sleep 600 ".
parameter second service call client exit505
------------- --------------------- ----------------------
default(30s) 180s (3m) 480s (8m)
receiv + 13 258s (4m18s) 558s (9m18s)
receiv + 15 270s (4m30s) 510s (8m30s)
receiv + 17 282s (4m42s) 522s (8m42s)
receiv + 70 300s (5m) 600s (10m)
receiv + 100 450s (7m30s) 690s (11m30s)
: data collected for http time out & retry
As seen in the table above, using the default parameters for creating HttpClient, the second service call occurs after 180s has elapsed. The 1st to 4th rows indicate that there is a multiple of 6 between the constructor receive timeout parameter and the timing of the second service call. However, the 5th and 6th rows indicate that there are no consistency.
Could you help to comment how to set up the receive timeout properly ?
Many thanks !
Thank you for your efforts ! IIRC, to realize a pointer to external function in Delphi (and FPC as well), one does not use external keyword but SafeLoadLibrary followed by GetProcAddress. I have to try and return.
If the following two code blocks are equivalent to each other, the regex posted above could be used to automatically transform the hundreds of declarations in SpiderMonkey.pas
current
type TJS_Initialize = function : Boolean; cdecl;
var JS_Init: TJS_Initialize external SpiderMonkeyLib name 'SM_Initialize';
Delphi compatible
function SM_Initialize: Boolean; cdecl; external SpiderMonkeyLib name 'SM_Initialize';
type TJS_Initialize = function : Boolean; cdecl;
var JS_Init: TJS_Initialize = SM_Initialize; // external SpiderMonkeyLib name 'SM_Initialize';
The following regex could be helpful to alleviate the burden to support Delphi compatibility of SpiderMonkey.pas
(?gs)type\s([_A-Za-z]+)\s*=\s*(function|procedure)(.*?cdecl;)\s*var(\s[_A-Za-z]+):\s*([_A-Za-z]+)\s(external.*?;)
Is there any news on this ? As @array81 reported, latest mORMot and SyNode are incompatible with each other when Delphi is used.
Many thanks !
PS: I have just tried "FPC trunk SVN 40491; Lazarus trunk SVN 59757", as suggested from the documentation, and the same AV exists.
The latest mORMot is cross compiled to linux64 by FPC on Windows. The console log is shown below.
1.2. Low level types:
- RTTI: 443 assertions passed 363us
- Url encoding: 200 assertions passed 659us
- Encode decode JSON: 396,397 assertions passed 132.40ms
- Variants: 64 assertions passed 212us
- Mustache renderer: 147 assertions passed 1.75ms
! Low level types - TDocVariant
! Exception EAccessViolation raised with messsage:
! Access violation
- TDecimal128: 17,446 assertions passed 1.89ms
- BSON: 245,068 assertions passed 50.87ms
100000 TBSONObjectID.ComputeNew in 49.84ms i.e. 2,006,179/s, aver. 0us
- TSynTableStatement: 221 assertions passed 213us
- TSynMonitorUsage: 1,202 assertions passed 212us
Total failed: 0 / 732,939 - Low level types PASSED 714.93ms
The line indicated from the file log is shown below.
TSynTestEvent(C.TestMethod[t])(); // run tests + Check() and TestFailed()
The version of fpc is
C:\fpcupdeluxe\fpc\bin\i386-win32 > fpc
Free Pascal Compiler version 3.3.1-r39711 [2018/09/08] for i386
Could you check this ? Many thanks !
PS: The same mORMot compiled by FPC on Windows as a Windows executable runs flawlessly.
SynLog in the latest version cannot compile under Delphi 7. It seems that anonymous cannot create ticket any more. The two lines are thus shown below. Could you fix this ?
code line no
before R.Seek(PtrUInt(P)-PtrUInt(R.MappedBuffer));
now R.Seek(P-PByte(R.MappedBuffer)); 1736
before W.WriteDirectEnd(PtrUInt(P)-Beg);
now W.DirectWriteFlush(P-Beg,tmp); 2044
Since many of us use mORMot as a service I decided to provide small tutorial how to say Windows to format log messages correctly during Windows log output
...
Hope it help for somebody...
Can you help to inform how to work with linux logs, i.e., append to /var/log/messages or /var/log/the_subject_app ? Many thanks !
MSEide+MSEgui has been updated to 4.6.2. Could you help to comment whether it is possible to use mORMot with the current MSEide+MSEgui ?
Just curious: do you plan not to fix it ?
Latest mORMot.pas does not compile using latest FPC for both i386-win32 and x86_64-linux. The line and msg shown are below.
Line 56264: ParamName := @VMP^.Name;
mORMot.pas(56264,30) Error: Variable identifier expected
The reason is in FPC 3.3.1-r39711 (installed using fpcupdeluxe), Name becomes a property. NamePtr should be used.
ParamName := VMP^.NamePtr;
PS: It seems that anonymous cannot create ticket now ? There is no create ticket on the tickets link.
Dealing with floating with big precision is always a problem. In our applications we try to avoid it by limiting precision part to maximum 4 digits and adding division(factor). For example if we need to store 8 digits precision (0.66599631) we store 2 field {value: 6659.9631, factor: 10000}. Hope it's help
Many thanks for your clever experience ! Is it because a number with maximum 4 digits can be exactly represented as Currency type ?
Yes, this is a known inconsistency, since they have two diverse implementations.
Sad to hear. It should be noted that ExtendedToStr behaves also inconsistently between Delphi25Tokyo's Win32 and Win64.
As shown in the sample code on gist , the ExtendedToStr behaves inconsistently between FPC and Delphi. Could you help to suggest the workaround ?
PS: FloatToStrF and Format seem to behave inconsistent within FPC, and between FPC and Delphi.
As shown in the gist, I am trying to store a float field into a variant field of a TSQLRecord. When the Variant field is assigned by calling ObjectToJSON, it has the correct internals.
{"Val":1234.56789}
Furthermore, the DB file has the correct float internals.
However, when the TSQLRecord is retrieved back from DB, its variant field contains string-based internals. As a result, the float field cannot be parsed back by calling ObjectLoadJSON.
{"Val":"1234.56789"}
Could you help to comment whether it is possible to prevent the transformation of the float internal into string internal ?
Could you share the build environment for static\i386-win and static\x86_64-win64 .a files ? For example, the OS and the toolchain versions and the gcc switches ? Many thanks !
Dear ab, many thanks !
Could you be kind enough to comment where these text settings you mentioned, i.e., woEnumSetsAsText or twoEnumSetsAsTextInRecord, can be applied to force enum serialization as text, for example, in the gist sample ? I mean, switching from
(RecordSaveJSON(Rec, TypeInfo(TRec)));
to
(RecordSaveJSON(Rec, TypeInfo(TRec), True));
does not work.
From your documentation , it seems that the solution is to switch from text-based serialization and deserialization to callback-based ones. Sorry for the trouble.
From your documentation , it is suggested that "For other types (like enumerations or sets), you can simply use the unsigned integer types corresponding to the binary value, e.g. byte word cardinal Int64 (depending on the sizeof() of the initial value)." With such treatment, the updated gist code serializes the enum property within the record as unsigned integer.
Nevertheless, the enum property within the TObject can be serialized to human readable text, could you help to comment whether it is possible to serialize enum property within the record also as human readable text ?
{
"E": 0,
"K": "",
"V": 0
}
{
"ClassName":"TObj",
"E": "meFirst",
"K": "",
"V": 0
}
As shown in the gist code , the call to TTextWriter.RegisterCustomJSONSerializerFromText fails if the record definition contains enum type: ESynException 'Unregistered ptCustom for TJSONRecordTextDefinition.AddItem(E: TMYENUM)'.
Could you help to comment the correct way to deal with record type with enum property in JSON serialization ?
Many thanks !
Dear ab, it seems to me that the deserialization of the class fails because GetDynArray of an array prop backed by methods will set DynArray.fValue to nil, and consequently "From := DynArray.LoadFromJSON()" concludes all of the remaining JSON is invalid and jumps to the end of the JSON. Could you help to comment ?
Furthermore, the gist sample has been clarified. As shown in DeserializeClassWithPropertyBackedByMethods2.dpr, there is no two properties pointing the actual storage.
As shown in the sample, a class with an array prop backed by methods is serialized and then deserialized. The deserialization of the class fails because, since GetDynArray of that prop sets DynArray.fValue to nil, DynArray.LoadFromJSON concludes all of the remaining JSON is invalid and jumps to the end of the JSON. It would seem more proper if DynArray.LoadFromJSON only skips the very array property backed by methods instead of all the remaining JSON. Could you help to comment ?
As discussed in https://synopse.info/forum/viewtopic.php?id=164, ManyAdd/ManyDelete/ManySelect/SourceGet/DestGet can be used with one less parameter - the "current" fSourceID/fDestID.
That is to say, in about 184th line of the "mORMot and Open Source friends-d573e7358a/SQLite3/Samples/ThirdPartyDemos/Migajek/synopse-sqlite-demo/Unit1.pas", both lines below should work. Nevertheless, only the second one works in the latest source.
cust.Tasks.ManyAdd(globalClient, task.ID, true)
cust.Tasks.ManyAdd(globalClient, cust.ID, task.ID, true)
Could you help to comment whether the situation is made clearer ?
Sorry for the confusion.
I did not manage to find the fix...
The diff was intended to show how to reproduce the regression . Could you help to check and fix ?
As discussed in https://synopse.info/forum/viewtopic.php?id=164, ManyAdd/ManyDelete/ManySelect/SourceGet/DestGet can be used with one less parameter - the "current" fSourceID/fDestID.
However, in the latest sample code "mORMot and Open Source friends-d573e7358a\SQLite3\Samples\ThirdPartyDemos\Migajek\synopse-sqlite-demo", the overloaded methods with one less parameter does not work, as tested with the following modifications
HiWin10@MICROSO-8C7VFP4 /cygdrive/d/mORMot and Open Source friends-d573e7358a/SQLite3/Samples/ThirdPartyDemos/Migajek/synopse-sqlite-demo
$ diff Unit1.pas ../synopse-sqlite-demo.original/
184,185c184
< ACustomer.Tasks.DestGet(globalClient, fIds);
< // ACustomer.Tasks.DestGet(globalClient, ACustomer.ID, fIds);
---
> ACustomer.Tasks.DestGet(globalClient, ACustomer.ID, fIds);
371,372c370
< cust.Tasks.ManyAdd(globalClient, task.ID, true)
< // cust.Tasks.ManyAdd(globalClient, cust.ID, task.ID, true)
---
> cust.Tasks.ManyAdd(globalClient, cust.ID, task.ID, true)
374,375c372
< cust.Tasks.ManyDelete(globalClient, task.ID);
< // cust.Tasks.ManyDelete(globalClient, cust.ID, task.ID);
---
> cust.Tasks.ManyDelete(globalClient, cust.ID, task.ID);
Could you help to fix the behavior ? Many thanks for your efforts !
Thank you very much for your helpful suggestions and comments !
PS: SM52 + Linux sounds quite interesting ! Great contributions from you !
The exception is seen with Win XE8/XE Tokyo, x32, SM45.
When SM52 is defined, the exception is not seen. Many thanks for your instructive test. Nevertheless, could you help to comment whether SM45 is deprecated in your company ?
With the latest mORMot & SyNode, the sample "02 - Binding" raises exception of "invalid arrow-function arguments (parentheses around the arrow-function may help)" during startup. Could you help to fix it ?
In addition to the post above, fLastClass needs to be reset to nil after removal...
procedure TJSONSerializerRegisteredClass.RemoveOnce(aItemClass: TClass);
begin
fSafe.Lock;
try
if PtrUIntScanExists(pointer(List),Count,PtrUInt(aItemClass)) then begin
Remove(aItemClass);
if fLastClass = aItemClass then
fLastClass := nil;
end;
finally
fSafe.UnLock;
end;
end;
When utilizing JSON serialization, one would try and test different versions of a specific class (isolated in different units). Under such circumstances, UnRegisterClassForJSON methods could be useful
....
procedure RemoveOnce(aItemClass: TClass);
....
class procedure UnRegisterClassForJSON(aItemClass: TClass); overload;
class procedure UnRegisterClassForJSON(const aItemClass: array of TClass); overload;
....
procedure TJSONSerializerRegisteredClass.RemoveOnce(aItemClass: TClass);
begin
fSafe.Lock;
try
if PtrUIntScanExists(pointer(List),Count,PtrUInt(aItemClass)) then
Remove(aItemClass);
finally
fSafe.UnLock;
end;
end;
....
class procedure TJSONSerializer.UnRegisterClassForJSON(aItemClass: TClass);
begin
if JSONSerializerRegisteredClass=nil then
GarbageCollectorFreeAndNil(JSONSerializerRegisteredClass,
TJSONSerializerRegisteredClass.Create);
JSONSerializerRegisteredClass.RemoveOnce(aItemClass);
end;
class procedure TJSONSerializer.UnRegisterClassForJSON(const aItemClass: array of TClass);
var i: integer;
begin
for i := 0 to high(aItemClass) do
UnRegisterClassForJSON(aItemClass[i]);
end;
In the source code shown, SynTests is slightly modified to generate dunit-report.xml. The generated file can be useful for automating the testing of multiple executable files.
Can you help to review and include the changes ?
When using DUnit, a dunit-report.xml containing test statistics will be generated. This can be useful for automating the testing of multiple executable files, because one can use tools (e.g., FinalBuilder) to check whether there are failures during automated tests.
Therefore, could you help to add the functionality of generating dunit-report.xml to SynTests.pas ?
...
And the root cause of the problem is that a record published property has no RTTI in the property field of the class, anyway. So adding a ref-counted field won't be enough...
Many thanks for your knowledgeable comments !
However, for the "won't be enough" part, as shown in a trivial example which is compilable under D7 and a SO post, it seems that adding a ref-counted field will be enough to generate RTTI for the record, for other records that contain it, and for the class that somehow publishes the record. Could you comment about or a possible counter-example ?
In D7, you could try to define an additional ref-counted field within the record to force rtti generation for your simple record types.
See http://codeverge.com/embarcadero.delphi … pe/1040908
See example of serializing record in "10.1.3.2.4. Text-based definition"
It seems that TSQLRecordMany.FillMany and TSQLRecordMany.DestGetJoined provide rather similar functionality: retrieving dest items related to certain source item. Could you help to comment the difference between them ? For example, what are the different situations where either FillMany or DestGetJoined fits better ?
After playing around a bit, now the difference becomes more clear to me... Sorry for the trouble!
...ThirdPartyDemos\Migajek\synopse-sqlite-demo...
procedure TForm1.LoadTasksForCustomer(const ACustomer: TSQLCustomer; const AList: TStrings);
var
tasks: TSQLTasks;
MsgUTF8: RawUTF8;
Msg: string;
task: TSQLTask;
fIds: TIDDynArray;
begin
// Can be created directly
tasks := TSQLTasks.Create;
try
// How to get Dest IDs
// tasks.DestGet(globalClient, fIds); // Does not work
tasks.DestGet(globalClient, ACustomer.ID, fIds); // Works
// ACustomer.Tasks.DestGet(globalClient, ACustomer.ID, fIds); // Original
task:= TSQLTask.CreateAndFillPrepare(globalClient, TInt64DynArray(fIds));
AList.BeginUpdate();
AList.Clear();
try
while task.FillOne do
AList.AddObject(Format('%s', [UTF8ToString(task.Text)]), Pointer(task.id));
finally
AList.EndUpdate();
FreeAndNil(task);
end;
// How to get Pivot instances, and Dest instances later
tasks.FillMany(globalClient, ACustomer.ID);
while tasks.FillOne do begin
// MsgUTF8 := tasks.Dest.Text;
task := TSQLTask.Create(globalClient, tasks.Dest.ID);
try
MsgUTF8 := task.Text;
Msg := UTF8ToString(MsgUTF8);
Msg := 'Get Dest through Pivot: ' + UTF8ToString(MsgUTF8);
OutputDebugString(PChar(Msg));
finally
task.Free;
end;
end;
// How to get Dest instances directly
task := tasks.DestGetJoined(globalClient, '', ACustomer.ID) as TSQLTask;
try
while task.FillOne do begin
MsgUTF8 := task.Text;
Msg := 'Directly get Dest: ' + UTF8ToString(MsgUTF8);
OutputDebugString(PChar(Msg));
end;
finally
task.Free;
end;
finally
tasks.Free;
end;
end;
procedure TForm1.lbTasksClick(Sender: TObject);
var
tasks: TSQLTasks;
task: TSQLTask;
cust: TSQLCustomer;
clientsIds: TIDDynArray;
i, j: integer;
MsgUTF8: RawUTF8;
Msg: string;
begin
gbEditTask.Visible:= lbTasks.ItemIndex <> -1;
if not gbEditTask.Visible then
exit;
gbEditTask.Visible:= lbTasks.Items.Objects[lbTasks.ItemIndex] <> nil;
if not gbEditTask.Visible then
exit;
task:= LoadTask(integer(lbTasks.Items.Objects[lbTasks.ItemIndex]));
cbTaskPriority.ItemIndex:= Ord(task.Priority);
cbTaskPriority.Tag:= task.ID;
FillCustomersList(CheckListBox1.Items, true);
CheckListBox1.Tag:= task.ID;
// load list of customers assigned to the given task
cust:= TSQLCustomer.Create();
try
cust.Tasks.SourceGet(globalClient, task.ID, clientsIds);
for i:= low(clientsIds) to high(clientsIds) do
begin
// find the client on the list (by ID)
for j:= 0 to CheckListBox1.Count -1 do
if Integer(CheckListBox1.Items.Objects[j]) = clientsIds[i] then
CheckListBox1.Checked[j]:= true;
end;
finally
cust.Free();
FreeAndNil(task);
end;
// Learn about FillManyFromDest
tasks := TSQLTasks.Create;
try
tasks.FillManyFromDest(globalClient, integer(lbTasks.Items.Objects[lbTasks.ItemIndex]));
while tasks.FillOne do begin
cust := TSQLCustomer.Create(globalClient, tasks.Source.ID);
try
MsgUTF8 := cust.FirstName;
Msg := UTF8ToString(MsgUTF8);
Msg := 'Get Source through Pivot: ' + UTF8ToString(MsgUTF8);
OutputDebugString(PChar(Msg));
finally
cust.Free;
end;
end;
finally
tasks.Free;
end;
end;
Another typo
/// retrieve all records associated to a particular Dest record, which
// has a TSQLRecordMany property
// - returns the Count of records corresponding to this aSource record <<<<<<<<<<<<<<<<<<<<<<< "this aSource record" should be "this aDest record" ?
// - use a "for .." loop or a "while FillOne do ..." loop to iterate
// through all Dest items, getting also any additional 'through' columns <<<<<<<<<<<<<<<<<<<<<<< "through all Dest items" should be "through all Source items" ?
// - the optional aAndWhereSQL parameter can be used to add any additional
// condition to the WHERE statement (e.g. 'Salary>:(1000): AND Salary<:(2000):')
// according to TSQLRecordMany properties - note that you should better use
// inlined parameters for faster processing on server, so you may call e.g.
// ! aRec.FillManyFromDest(Client,DestID,FormatUTF8('Salary>? AND Salary<?',[],[1000,2000]));
function FillManyFromDest(aClient: TSQLRest; aDestID: TID;
const aAndWhereSQL: RawUTF8=''): integer;
Moreover, would you also include test for FillManyFromDest in SynSelfTests.pas ?
https://synopse.info/files/html/Synopse … 01.18.html
...
5.5.2.2.1. Introducing TSQLRecordMany
...
for i := 1 to high(dID) do
begin
Check(MS.DestList.SourceGet(aClient,dID[i],res));
if not Check(length(res)=1) then <<<<<<<<<<<<<<<<<<<<<<<<<<< should be CheckFailed as shown in SynSelfTests.pas
Check(res[0]=sID[i]);
Check(MS.DestList.ManySelect(aClient,sID[i],dID[i]));
Check(MS.DestList.AssociationTime=i);
end;
for i := 1 to high(sID) do
begin
Check(MS.DestList.DestGet(aClient,sID[i],res));
if Check(length(res)=1) then <<<<<<<<<<<<<<<<<<<<<<<<<<< should be CheckFailed as shown in SynSelfTests.pas
continue; // avoid GPF
Check(res[0]=dID[i]);
Many thanks for your help !
It works after the following change:
...
{$IFDEF FPC}
plain := mmoPlain.Text;
{$ELSE ~FPC}
plain := StringToUTF8(mmoPlain.Text);
{$ENDIF ~FPC}
...
{$IFDEF FPC}
mmoPlain.Text := plain;
{$ELSE ~FPC}
mmoPlain.Text := UTF8ToString(plain);
{$ENDIF ~FPC}
...