You are not logged in.
Please try with
https://github.com/synopse/mORMot2/commit/c6322d491
The Commit is incomplete. fForceDateWithMS is still present in unit mormot.db.sql.zeos.
With best regards
Thomas
mORMot2, Commit 2.3.12796
fUseCache is used in the mormot.db.sql.zeos and mormot.db.sql.ibx units. After switching to enums, fUseCache is no longer available.
With best regards
Thomas
Delphi 13.0, Windows 64 Bit
Lazarus 4.1, FPC 3.3.1, Windows 64 Bit
mORMot 2, Commit 2.3.12723
The problem arises in unit mormot.core.log. Function SynLogException is called.
procedure SynLogException(const Ctxt: TSynLogExceptionContext);
...
// minimal exception logging to all other TSynLog files (to ease debug)
for i := 0 to high(SynLogFamily) do
begin
fam := SynLogFamily[i];
if (fam <> HandleExceptionFamily) and // if not already logged above
(Ctxt.ELevel in fam.Level) then
try
DoLogException(fam.fGlobalLog, Ctxt);
exceptFunction DoLogException calls LogHeaderNoRecursion(Log.fWriter, Ctxt.ELevel, @nfo^.CurrentTimeAndThread). Log.fWriter is Nil here. The LogHeaderNoRecursion function looks like this:
procedure LogHeaderNoRecursion(WR: TJsonWriter; const Level: TSynLogLevel;
TimeStampAndThreadNum: PShortString);
{$ifdef HASINLINE} inline; {$endif}
begin
WR.AddShort(TimeStampAndThreadNum^);Here, WR is not checked for Nil and EAccessViolation is triggered. The same behaviour occurs with Lazarus/FPC Windows 64-bit. The call stack shows that it starts via the SynLogVectoredHandler function. Delphi Windows 32-bit runs with the same source code without any problems because this function is not called.
With best regards
Thomas
I can confirm that it also works as desired with Delphi 12.2 and mORMot2 Commit 2.3.12685. The error has been fixed between Commit 2.3.11508 and 2.3.12685.
With best regards
Thomas
I can confirm that with mORMot 2, Commit 2.3.12684, it runs with the following Delphi versions: Delphi 10.2, Delphi 10.3, Delphi 10.4, Delphi 11.3, Delphi 12.3, Delphi 13.0.
I will test it with the latest mORMot version and Delphi 12.2 on Monday. It may be that we only need to update our mORMot version in the production version. I'm sorry I didn't test it beforehand.
With best regards
Thomas
Delphi 12.2
mORMot 2, Commit 2.3.11508
Shouldn't we check whether the content fits in the buffer before using the MoveFast function?
procedure TJsonWriter.AddFmt(Format: PUtf8Char; Values: PVarRec; ValuesCount: integer;
Escape: TTextWriterKind; WriteObjectOptions: TTextWriterWriteObjectOptions);
var
start: PUtf8Char;
Len: PtrInt;
begin
...
Len := Format - start;
if Len <> 0 then
begin
if BEnd - B <= Len then // note: PtrInt(BEnd - B) could be < 0
FlushToStream;
MoveFast(start^, B[1], Len);
With best regards
Thomas
Delphi 12.2
mORMot 2, Commit 2.3.11508
In the following test, I would have expected the result "True", but got "False". Where is the error?
var
va1, va2: TVariantDynArray;
begin
SetLength(va1, 2);
va1[0] := 'true';
va1[1] := 'false';
SetLength(va2, 2);
va2[0] := 'True';
va2[1] := 'False';
ShowMessage(DynArrayEquals(TypeInfo(TVariantDynArray), va1, va2, Nil, Nil, {CaseInsensitive=}True).ToString(TUseBoolStrs.True));It works with TRawUtf8DynArray.
With best regards
Thomas
Check TTextWriterWriteObjectOption
Permanent
initialization
Rtti.ByTypeInfo[TypeInfo(TProductForUploadAttribute )].Props.NameChange('customAttributeValue', '');With Custom-Serializer in this example.
With best regards
Thomas
Are there plans to support math functions in SQLite (DSQLITE_ENABLE_MATH_FUNCTIONS) in mORMot?
With best regards
Thomas
Delphi 12.2; mORMot Commit 2.3.11508; 32-Bit
The following example shows a behaviour that I did not expect:
var
v: Integer;
k1, k2, k3: Variant;
dic: IKeyValue<Variant, Integer>;
begin
k1 := Byte(1);
k2 := Byte(1);
k3 := Int64(1);
dic := Collections.NewKeyValue<Variant, Integer>;
dic.Add(k1, 2);
ShowMessage(dic.TryGetValue(k2, v).ToString(TUseBoolStrs.True)); // Result: True
ShowMessage(dic.TryGetValue(k3, v).ToString(TUseBoolStrs.True)); // Result: False <==In contrast, Delphi's TDictionary<Variant, Integer> delivers the expected result.
With best regards
Thomas
Here is the updated project with trailing comma removed.
https://codefile.io/f/ZaODJjXb1M
I have no problem with the example. It worked in all scenarios.
Delphi 12.3 32/64Bit, Debug/Release, mORMot2 2.3.11026/2.3.11647.
With best regards
Thomas
Small extension for the functions StatusCodeToText(), StatusCodeToReason() and StatusCodeToShort() from unit mormot.core.os to output messages in your own language.
Extend the functions as follows:
const
HTTP_CODE: array[0..44] of Word = (
HTTP_SUCCESS,
HTTP_NOCONTENT, ...
type
PHttpStatusCodeMessages = ^THttpStatusCodeMessages;
THttpStatusCodeMessages = array[0..High(HTTP_CODE)] of RawUtf8;
function StatusCodeToText(Code: cardinal; StatusCodeMessages: PHttpStatusCodeMessages = Nil): PRawUtf8;
var
i: PtrInt;
begin
if StatusCodeMessages = Nil then
StatusCodeMessages := @HTTP_REASON;
...With a customised message definition, the message can be displayed in your own language:
const
HTTP_REASON_DE: THttpStatusCodeMessages = (
'OK', // HTTP_SUCCESS - should be first
'Kein Inhalt', // HTTP_NOCONTENT
'Temporäre Weiterleitung', // HTTP_TEMPORARYREDIRECT
...
);
ShowMessage(Utf8ToString(StatusCodeToText(HTTP_NOCONTENT, @HTTP_REASON_DE)^));The return for StatusCodeToShort could be a little too short with TShort47, TShort64 would be better.
With best regards
Thomas
My Question was concerning the Use of the Cast to PUtf8Char
Defined as: "class function TSynLog.Enter(const TextFmt: RawUtf8; ...", then "TSynLog.Enter(StringToUtf8(XYZ), ...".
Defined as: "class function TSynLog.Enter(const TextFmt: PUtf8Char; ...", then "TSynLog.Enter(PUtf8Char(StringToUtf8(XYZ)), ..." or "TSynLog.Enter(Pointer(StringToUtf8(XYZ)), ...".
With best regards
Thomas
Delphi 12.2
mORMot2, Commit: 2.3.10687
The SplitRights function does not do what the description says when the separator is one Byte long. The Exit statement was forgotten.
With best regards
Thomas
The description of the UpperCaseU function is:
/// fast conversion of the supplied text into 8-bit uppercase
// - this will not only convert 'a'..'z' into 'A'..'Z', but also accentuated
// latin characters ('e' acute into 'E' e.g.), using NormToUpper[] array
// - it will therefore decode the supplied UTF-8 content to handle more than
// 7-bit of ascii characters (so this function is dedicated to WinAnsi code page
// 1252 characters set)An example with following test:
var
ul, uh: RawUtf8;
begin
// Windows-1252 character set
// Ordinal numbers (decimal), https://de.wikipedia.org/wiki/Windows-1252
// - é (lower case): 233
// - É (upper case): 201
ul := UTF8Encode('étudiant');
uh := UTF8Encode('Étudiant');
// Result: ETUDIANT, Expected: ÉTUDIANT
ShowMessage(Utf8ToString(mormot.core.unicode.UpperCaseU(ul)));
// Result: etudiant, Expected: étudiant
ShowMessage(Utf8ToString(mormot.core.unicode.LowerCaseU(uh))); Do I misunderstand the text of the description, or do I misinterpret the name of the function, or is my expectation simply wrong?
With best regards
Thomas
Delphi 12.1
mORMot2, Commit 2.3.9849
Are the double separators in result desired at the end?
ShowMessage(MakePath(['e:\Test\', 'SubFolder\'], {EndWithDelim=} True)); // Result: e:\Test\SubFolder\\With best regards
Thomas
Hello Arnaud!
In VariantToVarRec() you can see that you don't need to allocate anything.
Your note about VariantToVarRec led me to a variation that works as expected. However, I am still puzzled by the behaviour of my version from the initial post. I would just like to mention that this solution is not production code either, but only serves the purpose of understanding.
function BuildLikeExpression(const aTermList: TVariantDynArray): RawUtf8; overload;
var
TermIdx: Integer;
TermArrayOfConst: TTVarRecDynArray;
TermLiteralArray: TRawUtf8DynArray;
PLikeTerm: PVarRec;
begin
SetLength(TermArrayOfConst, Length(aTermList));
SetLength(TermLiteralArray, Length(aTermList));
for TermIdx := 0 to High(TermArrayOfConst) do
begin
PLikeTerm := @TermArrayOfConst[TermIdx];
case TVarData(aTermList[TermIdx]).VType of
varString, varUString:
begin
TermLiteralArray[TermIdx] := VariantToUtf8(aTermList[TermIdx]);
PLikeTerm^.VType := vtAnsiString;
PLikeTerm^.VAnsiString := Pointer(TermLiteralArray[TermIdx]);
end;
varByte:
begin
PLikeTerm^.VType := vtInteger;
PLikeTerm^.VInteger := TVarData(aTermList[TermIdx]).VByte;
end;
end;
end;
Result := BuildLikeExpression(TermArrayOfConst);
end;With best regards
Thomas
Hello Arnaud!
Also note that BuildLikeExpression() needs to initialize their result := ''; before result := result + loop.
Thank you for your reply. Yes, it is secured in the original source code. I am a proponent of the concept of inherent security when writing functions. The example source code is just reduced to an absolute minimum so as not to violate the forum rules. I had the VariantsToArrayOfConst function in mind. I should have mentioned it, the source code is not the original production code but just an example of behaviour I don't understand.
With best regards
Thomas
Hallo!
Can someone tell me where I'm going wrong?
Delphi 12.1, mORMot2 Commit 9637
I have an unexpected behaviour in the following source code. I know how to work around the error or rewrite the source code so that it works correctly, but I don't understand why the following doesn't work. There is an overloaded BuildLikeExpression function. In one function, the parameter is passed as an "array of const" and in the second as a "VariantDynArray". The function with the VariantDynArray converts the input parameter into a VarRecDynArray and calls the function with the corresponding type.
If the input parameter VariantDynArray starts with a TSqlLikePlaceholder, the following happens: The array is converted into a VarRecDynArray and the corresponding function is called with it. The problem is the first VarRec record with a string. During processing in the loop Result := Result + LIKE_WILDCARD[TPpSqlLikePlaceholder.xxx], the value Result appears in this record after the assignment. This only happens in this constellation and only for the first VarRec record with a string. All similar sequences of parameters in the following and all other combinations work as expected. If I cast the first string parameter to RawUtf8, the effect does not occur. It is no problem to rewrite the function to handle normal strings or to make the cast, but in my opinion it should also work like this.
Function with parameter "array of const":
function BuildLikeExpression(const aTermList: array of const): RawUtf8; overload;
const
LIKE_WILDCARD: array[TSqlLikePlaceholder] of RawUtf8 = ('%', '_');
var
TermIdx: Integer;
TermLiteral: RawUtf8;
PLikeTerm: PVarRec;
begin
for TermIdx := 0 to High(aTermList) do
begin
PLikeTerm := @aTermList[TermIdx];
case PLikeTerm^.VType of
vtString, vtAnsiString, vtUnicodeString:
begin
VarRecToUtf8(PLikeTerm^, TermLiteral);
Result := Result + TermLiteral;
end;
vtInteger:
case PLikeTerm^.VInteger of
Ord(TSqlLikePlaceholder.Any):
Result := Result + LIKE_WILDCARD[TSqlLikePlaceholder.Any];
Ord(TSqlLikePlaceholder.One):
Result := Result + LIKE_WILDCARD[TSqlLikePlaceholder.One];
end;
end;
end;
Result := ' LIKE ' + mormot.core.unicode.QuotedStr(Result);
end;Function with parameter "VariantDynArray":
function BuildLikeExpression(const aTermList: TVariantDynArray): RawUtf8; overload;
var
TermIdx: Integer;
TermLiteral: RawUtf8;
TermArrayOfConst: TTVarRecDynArray;
PLikeTerm: PVarRec;
begin
SetLength(TermArrayOfConst, Length(aTermList));
for TermIdx := 0 to High(TermArrayOfConst) do
begin
PLikeTerm := @TermArrayOfConst[TermIdx];
case TVarData(aTermList[TermIdx]).VType of
varString, varUString:
begin
TermLiteral := VariantToUtf8(aTermList[TermIdx]);
PLikeTerm^.VType := vtAnsiString;
PLikeTerm^.VAnsiString := Pointer(TermLiteral);
end;
varByte:
begin
PLikeTerm^.VType := vtInteger;
PLikeTerm^.VInteger := TVarData(aTermList[TermIdx]).VByte;
end;
end;
end;
Result := BuildLikeExpression(TermArrayOfConst);
end;These are the results:
// Result: LIKE '_Test%BlaBla_' <== OK (with parameter "array of const")
BuildLikeExpression([Ord(TSqlLikePlaceholder.One), 'Test', Ord(TSqlLikePlaceholder.Any), 'BlaBla', Ord(TSqlLikePlaceholder.One)])
// Result: LIKE '__%BlaBla_' <== False (with parameter "VariantDynArray")
BuildLikeExpression([TSqlLikePlaceholder.One, 'Test', TSqlLikePlaceholder.Any, 'BlaBla', TSqlLikePlaceholder.One])
// Result: LIKE '_Test%BlaBla_' <== OK (with parameter "VariantDynArray")
BuildLikeExpression([TSqlLikePlaceholder.One, RawUtf8('Test'), TSqlLikePlaceholder.Any, 'BlaBla', TSqlLikePlaceholder.One])With best regards
Thomas
mORMot2, Version 2.3.8840, Delphi 12.1, 32Bit
Function TTextWriter.SetText does not behave consistently. The result for case 1 and case 2 are different.
var
w: TJsonWriter;
b: TTextWriterStackBuffer;
m: TMemoryStream;
s: RawUtf8;
begin
// 1) Case TTextWriterStackBuffer
w := TJsonWriter.CreateOwnedStream(b);
try
w.Add(True);
w.AddComma;
w.SetText(s);
w.Add(64);
w.SetText(s);
ShowMessage(Utf8ToString(s)); // Result: 64
finally
w.Free;
end;
// 2) Case TMemoryStream
m := TMemoryStream.Create;
try
w := TJsonWriter.Create(m);
try
w.Add(True);
w.AddComma;
w.SetText(s);
w.Add(64);
w.SetText(s);
ShowMessage(Utf8ToString(s)); // Result: true,64
finally
w.Free;
end;
finally
m.Free;
end;The behaviour differs depending on the buffer type and I can't find a description for it.
With best regards
Thomas
The first time I promoted mORMot, I would have shared the points from the opening post. Later I thought, no matter what you say, many developers don't want to rethink. Today I promote mORMot with the slogan: It's on your computer in 3 minutes and removed without a trace in 3 seconds if you don't like it. Often I show concrete source code including the uses part. So it works with copy&paste.
I therefore tend to agree with @squirrel, but I'm not someone who knows anything about marketing.
With best regards
Thomas
I don't want to use "model", but for testing I added
CreateWithOwnModel([TAuthGroup, TAuthUser], {HandleUserAuthentication=} True);like the example, but then I got errors:
You have to write:
CreateWithOwnModel([TAuthGroup, TFileAuthUser], {HandleUserAuthentication=} True, ROOT_NAME_FILE);Compatibility with Delphi 7 should be possible. Remove the inline variables. You can rewrite the function u_ServiceUtils.CheckFileName with mORMot's own functions to get rid of TPath. You can replace TWebBrowser component with a TMemo. Then only text files are possible.
With best regards
Thomas
What is wrong with me??
The order of call is incorrect. You must call CreateMissingTables first and then CreateSqlMultiIndex. Look in unit test.orm.extdb.pas to see how it is implemented there. Or do it in the function InitializeTable of the ORM object.
With best regards
Thomas
That _Safe could return a read only fake DocVariantData is perhaps a little bit too much and contrary to the name „_safe“.
No. That is why you can write:
var
v: Variant;
isPeaPuree: Boolean;
begin
isPeaPuree := _Safe(v).O_['One'].O_['Two'].O_['Three'].B['IsPeaPuree'];
ShowMessage(IsPeaPuree.ToString(TUseBoolStrs.True));You can easily reproduce the problem with the Init function. Switch on ReportMemoryLeaksOnShutdown. Write the following:
var
names: TRawUtf8DynArray;
begin
names := ['one', 'two'];
names := Nil;Set a Breakpoint at names := Nil. When you have reached this point during debugging, press Ctrl+Alt+D and open the disassembly window. Now you can see that the compiler has automatically inserted a call to DynArrayClear for this Nil assignment. However, if you write Pointer(names) := Nil, this does not happen and the dynamic array can no longer be cleaned up at the end. In this case, FastMM will report the lost memory.
With best regards
Thomas
How should I call clear here? What would clear do with an empty []?
v.Clear;
TDocVariantData(v).Clear;Set breakpoints as described in article under point 3. Then you can see what is going on.
With best regards
Thomas
However, I keep getting the error => TOrmTableDataSet3: Cannot modify a read-only dataset.
What is your question?
With best regards
Thomas
Can't find your article.
You must follow the link in the announcement. I write my posts in Delphi-Praxis Forum.
With best regards
Thomas
Regarding the servers, I realized that when I use TRestServerDB, the user or client can easily access the tables. Authentication might prevent this access, but from what I’ve studied, it doesn’t seem to check the access details. For example, imagine I have a "status" field that marks records as deleted (without physically deleting them). In the method for listing data, I don’t send these records, but since the client has access to the table through ORM, they can retrieve those records.
Also, when I use TRestServerFullMemory, I no longer have access to the ORM.
You don't read the answers to your questions carefully. You're making it difficult for everyone here to help you. You have already received useful links for the start in post #2. Have you studied this example, which only uses an HTTP server? Once you have found the entry point, you can search the mORMot source code for it and quickly find the answer to your questions. This example only uses a REST server and shows the use of the ORM. These two examples (here and here) show how REST and HTTP servers work together without using the ORM. Why should it be necessary to expose access to the ORM? Use TRestServerFullMemory as Main and only open access to the ORM in the interface-based services. You can combine everything and have already been shown how to do it here. Please let us help you too. Read through the tips you have received again.
With best regards
Thomas
As you can see in the code above, I pass the dbconnection to the InvoiceService constructor.
You must keep in mind that with the method you used to register the services, they are always sicShared. This is unimportant in a simple example, but you must take it into account for a server. But it is not necessary and with a little preparation you can implement it as shown in some examples.
Define your RestServer as follows:
type
TMainRestServer = class(TRestServerFullMemory)
strict private
FConnectionPool: TSqlDBOdbcConnectionProperties;
public
property ConnectionPool: TSqlDBOdbcConnectionProperties
read FConnectionPool;CustomService as follows:
type
ICustomService = interface(IInvokable)
function GetConnection(out pmoConnection: TSqlDBConnection): Boolean;
end;
type
TCustomServiceObject = class(TInjectableObjectRest, ICustomService)
protected
function GetConnection(out pmoConnection: TSqlDBConnection): Boolean;
function TCustomServiceObject.GetConnection(out pmoConnection: TSqlDBConnection): Boolean;
begin
Result := False;
if TMainRestServer(Server).ConnectionPool <> Nil then
begin
pmoConnection := TMainRestServer(Server).ConnectionPool.ThreadSafeConnection;
Result := (pmoConnection <> Nil);
end;
end;And use it in your services as follows:
var
json: RawUtf8;
dbConn: TSqlDBConnection;
dbStmt: ISqlDBStatement;
begin
if GetConnection(dbConn) then
begin
dbStmt := dbConn.NewStatementPrepared(...);
dbStmt.ExecutePreparedAndFetchAllAsJson(False, json);With best regards
Thomas
mORMot2, Commit 7777, Delphi 12.1, 32Bit
Access violation for these combinations in function ObjArrayDelete().
...ObjArrayHelper.Delete(aIndex: Integer): Boolean;
var
Count: Integer;
begin
Count := Length(Self);
if (Count > 0)
and (aIndex >= 0)
and (aIndex < Count) then
begin
// Ok
ObjArrayDelete(Self, aIndex);
// Ok
ObjArrayDelete(Self, Self[aIndex]);
// raised exception class EAccessViolation with message 'Access violation at address 00FBCB6E. Read of address 70505408'.
ObjArrayDelete(Self, Count, Self[aIndex]);
// raised exception class EAccessViolation with message 'Access violation at address 00A2CB6E. Read of address 70505408'.
ObjArrayDelete(Self, aIndex, False, @Count);With best regards
Thomas
I reviewed the mormot2 examples. In the examples, everyone uses the model.
Why is this a problem?
With best regards
Thomas
uses
mormot.core.base,
mormot.core.data,
mormot.core.os,
mormot.net.client;
var
content: RawByteString;
begin
content := HttpGet('https://tile.openstreetmap.org/10/549/339.png');
if content <> '' then
FileFromString(content, ChangeFileExt(ExeVersion.ProgramFileName, '.png'));With best regards
Thomas
Unit: mormot.core.unicode, commit 7584
Function: function TrimLeft(const S: RawUtf8): RawUtf8;
There should be a +1 at the very end of the last line, otherwise one character too few will be returned.
if i = 1 then
result := S
else
FastSetString(result, @PByteArray(S)[i - 1], l - i + 1);With best regards
Thomas
How to access a separate TRestServerDB instance?
You can find posts on this topic in this forum.
type
TCustomServiceObject = class(TInjectableObjectRest, ICustomService)
protected
function InternalAdd(const pmcItem: TOrmCustomRecord; pmForceID: Boolean = False): TID;
public
function GetDBDataRestServer: TRestServerDB; overload;
function GetDBBlobRestServer: TRestServerDB; overload;
end;
function TCustomServiceObject.InternalAdd(const pmcItem: TOrmCustomRecord; pmForceID: Boolean): TID;
var
restServer: TRestServerDB;
begin
restServer := GetDBDataRestServer;
if (restServer <> Nil) and (pmcItem <> Nil) then
Result := restServer.Server.Add(pmcItem, True, pmForceID)
else
Result := -1;
end;
function TCustomServiceObject.GetDBDataRestServer: TRestServerDB;
begin
with TRestAdminServer(Server) do
Result := RestServerPool.FindRestServer(GetDBDataRestServerID(ServiceRunningContext.Request.Session));
end;
function TCustomServiceObject.GetDBBlobRestServer: TRestServerDB;
begin
with TRestAdminServer(Server) do
Result := RestServerPool.FindRestServer(GetDBBlobRestServerID(ServiceRunningContext.Request.Session));
end;With the technique shown above, you can also manage several separate databases for each organization without any problems. If you only want to use several databases, you do not need a pool and can access them directly. Then the following is enough:
type
TServerMain = class(TSynPersistent)
private
FHttpServer: TVGSHttpServer;
FDBDataRestServer: TDBDataRestServer;
FDBBlobRestServer: TDBBlobRestServer;With best regards
Thomas
Anyway, I will revert it because it seems to be unneeded and unsafe - premature optimization or over optimization.
Which is the root of all evil, said.
Are you sure that's what you wanted?
else if (StartPos = 0) and
(Len = L) and
(PStrCnt(PAnsiChar(pointer(fDataString)) - _STRCNT)^ = 1) then
FastAssignUtf8(Text, fDataString) // fast return the fDataString instance
elseThe description says: "fast return the fDataString instance", but the second part of the sentence says: "and set src to Nil". After calling function FastAssignUtf8, "fDataString" is empty. The function should then better called: GetAsTextAndEmptyStreamIfLenIsSize(). ![]()
With best regards
Thomas
Commit 7267 tested and it works. Thank you very much and have a nice weekend.
With best regards
Thomas
I don't see when fThreads[] could be = 0, unless the log file input is corrupted / was not generated by TSynLog.
If remote logging is used (TRestHttpsClient.CreateForRemoteLogging and LogView), this may be the case.
With best regards
Thomas
mORMot2, Commit 7264 (fedb2ee), Delphi 12, 32Bit
Unit mormot.core.log
Line 7756 should be changed. It should be checked that (fThreads[Index] > 0), otherwise the call to function GetBitPtr could run into nothing.
function TSynLogFileView.Select(aRow: integer): integer;
...
for i := 0 to Count - 1 do
begin
if fLevels[i] in fEvents then
begin
if (fThreads = nil)
or GetBitPtr(pointer(fThreadSelected), fThreads[i] - 1) then // <-- here
beginWith best regards
Thomas
mORMot2, Commit 7243 (ee6c774), Delphi 12, 32Bit
Unit mormot.core.log
Line 7550 should be changed. The search should not be started from "fCount" but from "fSelectedCount".
function TSynLogFileView.SearchPreviousText(const aPattern: RawUtf8; aRow: integer): PtrInt;
...
// search from end
// for result := fCount - 1 downto aRow + 1 do <= This line should be changed
for result := fSelectedCount - 1 downto aRow + 1 do
if LineContains(aPattern, fSelected[result]) then
exit; With best regards
Thomas
This is not clear for me, but will my possible solution "map" on DB field to one vaiants property?
Do Mormot fw have such a example? ORM implementation widely found in examples, but this - not.
You can read this article in Delphi-Praxis forum. It can also be found in the examples. To query this field directly via SQL, it uses the SQLite syntax.
With best regards
Thomas
I'm currently refactoring our code to use mORMot version 2 and I can't find the function CurrentServiceContext (previously found on the mORMot.pas unit) to get the TServiceRunningContext.
Unit mormot.rest.server
ServiceRunningContext, or better use TInjectableObjectRest for Interface-based Services.
With best regards
Thomas
I think, I don't seem to know how to use THttpMultiPartStream and THttpClientSocket.Post function.
Please give me some advice.
Did you watch the stream before sending it? The following simple test looks ok to me:
var
mpStream: THttpMultiPartStream;
fileStream: THandleStream;
begin
mpStream := THttpMultiPartStream.Create;
try
mpStream.AddContent('Field01', '100', TEXT_CONTENT_TYPE);
mpStream.AddContent('Field02', '{"Name": "Thomas"}', JSON_CONTENT_TYPE);
mpStream.AddFileContent('Data', 'data.json', '{"Name": "Thomas","Nickname": "tbo"}', JSON_CONTENT_TYPE, 'binary');
mpStream.AddFile('Test', MakePath([Executable.ProgramFilePath, 'test.json']));
mpStream.AddFile('Image', MakePath([Executable.ProgramFilePath, 'image.png']));
fileStream := TFileStreamEx.Create(MakePath([Executable.ProgramFilePath, 'mpTest.dat']), fmCreate);
try
mpStream.Flush;
StreamCopyUntilEnd(mpStream, fileStream);
finally
fileStream.Free;
end;
finally
mpStream.Free;
end;With best regards
Thomas
Do you have more clue?
Sorry Arnaud, I made a stupid copy-paste mistake. Everything is fine with mORMot. The nasty thing was, the Delphi debugger totally misled me. I shouldn't work until the middle of the night. The only good thing was that I got some new ideas from all the debugging. Sorry again for the false alarm.
With best regards
Thomas