You are not logged in.
Pages: 1
Hello,
It appears that there is a bug in the TTextWriter.AddSpaced (mormot.core.text unit).
procedure TTextWriter.AddSpaced(const Text: RawUtf8; Width: PtrInt; SepChar: AnsiChar);
begin
AddSpaced(PUtf8Char(pointer(Text)), length(Text));
if SepChar <> #0 then
Add(SepChar);
end;
Internal call of the AddSpaced must have one more parameter: Width.
AddSpaced(PUtf8Char(pointer(Text)), length(Text), Width);
Otherwise a wrong overloaded method is called, which leads to infinite recursion and stack overflow.
Sergey
My fault! I had not pulled the latest version.
Unfortunately, this does not work: empty {LogName}.#.synlz files are created.
It looks like I have to either write a custom OnRotate handler, or write a "dummy" TAlgoCompress inherited class and assign its name to LogCompressAlgo.
Thank you!
Happy New Year to everybody!
The TSynLog.Family has the RotateFileNoCompression property in the mORMot1 but I have not found any analogue in the mORMot2.
Is there a way to rotate old logs without compression in mORMot2?
Sergey
Well, it appears that I was inattentive and looked in a wrong place...
I did not pay attention to the 'sqlite_autoindex_*' indexes. The SQLite automatically creates them during the table creation. So, the 'stored AS_UNIQUE' indexes are indeed created.
Actually, often duplicate indexes are created as well: 'sqlite_autoindex_*' (unique) and 'NDX{Table}{Field}' (non-unique). I suppose that the former are created by the SQLIte and the latter - by the mORMot.
And the same happens if a table is mapped to a MS SQL server: 'UO_{Table}_*' (unique) and 'NDX{Table}{Field}' (non-unique).
I apologize for the confusion.
Sergey
Thank you!
It appears that I have found the problem: it is in the unit mormot.core.rtti.
It seems to me that instead of
inc(Counts[Instance.Kind]);
the following code should be used in the procedure TRttiCustomList.AddToPairs(Instance: TRttiCustom; Info: PRttiInfo):
inc(Counts[Info.Kind]);
When the AddToPairs is called from the function TRttiCustomList.DoRegister
// initialize a new TRttiCustom/TRttiJson instance for this type
result := GlobalClass.Create;
// register ASAP to avoid endless recursion in FromRtti
AddToPairs(result, Info);
// now we can parse and process the RTTI
result.FromRtti(Info);
the first parameter (result) is not completely initialized yet and the Instance.Kind always equals to rkUnknown inside the AddToPairs. Therefore, the correct counters in the array are not incremented.
Hello,
Environment: Delphi XE2, TRestServerDB, SQLite3 database, 4 tables in a model, a table has a field defined as 'stored AS_UNIQUE'.
If the database is created in the constructor of the server
server := TRestServerDB.Create(aModel, 'c:\temp\test.db', False);
server.Server.CreateMissingTables;
then everything works as expected: the uniqueness index is created in the test.db.
But if connection properties and the OrmMapExternal are used
aConnProps := TSQLDBSQLite3ConnectionProperties.Create('c:\temp\test.db', '', '', '');
...
OrmMapExternal(aModel, [ list of tables ], aConnProps);
server := TRestServerDB.Create(aModel, SQLITE_MEMORY_DATABASE_NAME, False);
server.Server.CreateMissingTables;
then no index for 'AS_UNIQUE' field is created. It does not matter if the target database is SQLIte3 or MS SQL.
Best regards,
Sergey
Hello,
Environment: Delphi XE2, TRestServerDB, SQLite3 database, 4 tables in a model, one of the tables references another by a TClassNameToBeDeletedID field.
It appears that the TClassNameToBeDeletedID does not work as expected: the field index is not created and (I suspect) the cascade deletion does not work either.
I have found the immediate cause for this in the unit mormot.orm.core but deeper details elude me.
constructor TOrmPropInfoRttiTID.Create(aPropInfo: PRttiProp; aPropIndex: integer; aOrmFieldType: TOrmFieldType; aOptions: TOrmPropInfoListOptions);
...
if IdemPropName(TypeName^, 'TID') or
(ord(TypeName^[1]) and $df <> ord('T')) or // expect T...ID pattern
(PWord(@TypeName^[L - 1])^ and $dfdf <> ord('I') + ord('D') shl 8) or
(Rtti.Counts[rkClass] = 0) then
exit;
if (L > 13) and IdemPropName('ToBeDeletedID', @TypeName^[L - 12], 13) then
begin // 'TOrmClientToBeDeletedID' -> TOrmClient + CascadeDelete=true
fCascadeDelete := true;
Found := Rtti.FindName(@TypeName^[1], L - 13, rkClass);
end
...
end;
The condition (Rtti.Counts[rkClass] = 0) is False. If it is commented out then the code works as expected.
Best regards,
Sergey
Thank you!
Hello,
FHIR (a standard for health care data exchange) has a concept of an operation (http://hl7.org/fhir/operations.html). Name of an operation starts from '$' but IsValidUriRoute complains about this character.
So, I propose to add the '$' to the list of allowed characters in the IsValidUriRoute:
else if not (p^ in ['/', '_', '-', '.', '$', '0'..'9', 'a'..'z', 'A'..'Z']) then
exit; // not a valid plain URI character
Sergey
Thank you for the reply!
As I mentioned, I do not need bidirectional. So, I do actually have a choice.
As far as I can understand, the recommendations have been changed and the THttpClientWebSockets is the way to go now.
Hello,
I am confused about which client to use in the Windows environment.
I do not need bidirectional functionality of sockets. The mORMot framework SAD 1.18 says on page 332:
As stated above, there is still a potential performance issue to use the direct TSQLHttpClientWinSock
class over a network. It has been reported on our forum, and root cause was not identified yet.
Therefore, the TSQLHttpClient class maps by default to the TSQLHttpClientWinHTTP class. This is
the recommended usage from a Delphi client application.
However, in the mORMot 2 source code the TSQLHttpClient class maps by default to the TRestHttpClientSocket and there is a note reagrding "slower and less stable" in the source code (mormot.rest.http.client.pas):
{.$define CLIENTUSEWININET}
// force HTTP/1.1 RESTful JSON default mORMot Client class to use WinHttp
// - e.g. to try Windows built-in proxy settings
// - but is slower and less stable in some context (e.g. useHttpAsync)
Please, help me to sort this out.
Sergey
Hello,
It appears that there is a bug in the OnlyChar function (mormot.core.unicode.pas).
Instead of
for i := 0 to SizeOf(only) do
there should be
for i := 0 to SizeOf(only)-1 do
Otherwise, an exception occurs.
Sergey
Hello,
In the process of migrating my applications from moRMot1 to mORMot2, I created a replacement table. It simplifies code changes that are required if the PUREMORMOT2 is defined.
mORMot2 migration table (HTML)
The table is in no way complete but might help someone anyway.
Sergey
Hello,
I have been exploring functionality of the mORMot in regards to the MongoDB. And I have found that if I define a record like this:
TEmpl = class(TPersistent)
private
FCode: Integer;
FName: RawUTF8;
published
property Code: Integer read FCode write FCode;
property Name: RawUTF8 read FName write FName;
end;
TSQLMongoTest = class(TSQLRecord)
private
FEmpl1: TEmpl;
FEmpl2: Variant;
public
constructor Create; override;
destructor Destroy; override;
published
property Empl1: TEmpl read FEmpl1;
property Empl2: Variant read FEmpl2 write FEmpl2;
end;
and save it to a database:
TAutoFree.One(R, TSQLMongoTest.Create);
R.Empl1.Code := 23;
R.Empl1.Name := 'Empl1';
R.Empl2 := TDocVariant.NewObject(['Code', 23, 'Name', 'Empl2']);
Client.Add(R, True);
then the Empl2 is stored as a real "object" part of the MongoDB document but the Empl1 - as JSON in a string field:
{"_id":{"$numberInt":"1"},"Empl1":"{\"Code\":23,\"Name\":\"Empl1\"}","Empl2":{"Code":{"$numberInt":"23"},"Name":"Empl2"}}
After that, I can find the document using Eml2.Code but obviously not the Empl1.Code:
R := TSQLMongoTest.CreateAndFillPrepare(Client, 'Empl1.Code = ?', [23]);
try
while R.FillOne do
s := R.Empl1.Name; // Is not executed
finally
R.Free;
end;
R := TSQLMongoTest.CreateAndFillPrepare(Client, 'Empl2.Code = ?', [23]);
try
while R.FillOne do
s := R.Empl2.Name; // Executed
finally
R.Free;
end;
As far as I can understand, according to the documentation there should be no difference in my case between Empl2 and Empl1.
DocVariants are great! But I would like to use a predefined structure and compile-time checks as well. Is there a way to store a TObject/TPersistent/record property of a TSQLRecord descendant as an "object" part of a MongoDB document (not as JSON in a string field)?
Thank you in advance for any hints.
Best regards,
Sergey Gavrilov
Well, it does not work. But for a different reason.
When I try to run a SELECT query on an "external" SQLite3 table, it is not possible to reference the ID property with a table name.
This works:
tbl := Rest.List([TSQLDocument], 'ID,Name');
but this does not:
tbl := Rest.List([TSQLDocument], 'Document.ID,Document.Name');
The code raises an exception: 'Error SQLITE_ERROR (1) [SELECT Document.ID,Document.Name FROM Document] using 3.27.2 - no such column: Document.ID, extended_errcode=1'.
This code does not work either:
tbl := Rest.List([TSQLDocument], 'ID,Document.Name');
So the JOINs with IDs are not possible.
I tried to inherit the storage objects from TSQLRecordVirtualTableAutoID (this is not really necessary since the VirtualTableExternalRegister sets the Props.Kind := rCustomAutoID) but it did not help.
So, it appears that there is a bug somewhere...
As far as I can understand, if the default amLocked mode does not work for SQLite3 connections, I can at least try to put all ORM writes into a single thread:
aServer.AcquireExecutionMode[execORMWrite] := amBackgroundThread;
Therefore, all write operations will be queued and it will not matter whether the connections are really threadsafe or not. Am I right?
Hello,
I am starting a new project and going to use mORMot in it. I plan to start with a SQLite3 data base and see how it goes. If it is necessary, I will switch to an external database later.
Since I have some mostly static data (reference tables, users, patients) and potentially significant amounts of various documents to store, I would like to split the tables between several db-files.
Of course, I can use the REST redirection but in this case I will not be able to use redirected tables in JOINs. I can live without it, if this is absolutely necessary, but sometimes JOINs might be helpful/convenient. So, I am thinking about VirtualTableExternalRegister and TSQLDBSQLite3ConnectionProperties.
Something like a sample below (see the constructor TMyRest.Create). However, I was not able to find a definitive answer on thread-safety of this approach. Will this work correctly? Will data access requests be serialized to each db separately or to the whole SQLIte3 engine?
Thank you in advance.
Best regards,
Sergey
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
mORMot,
mORMotHTTPServer,
System.SysUtils,
SynSQLite3Static,
Unit2 in 'Unit2.pas';
var
MyRest: TMyRest;
begin
MyRest := TMyRest.Create(CreateModel);
try
with TSQLHttpServer.Create('80', [MyRest], '+', useHttpApiRegisteringURI) do
try
Writeln('Server started.');
Write('Press Enter to terminate...');
Readln;
Shutdown;
finally
Free;
end;
finally
FreeAndNil(MyRest);
end;
end.
unit Unit2;
interface
uses
mORMot, mORMotSQLite3, SynCommons, SynDBSQLite3;
type
TMyRest = class(TSQLRestServerDB)
private
FDocDBProps: TSQLDBSQLite3ConnectionProperties;
FRefDBProps: TSQLDBSQLite3ConnectionProperties;
public
constructor Create(AModel: TSQLModel);
destructor Destroy; override;
end;
TSQLBranch = class(TSQLRecord)
private
FName: RawUTF8;
published
property Name: RawUTF8 read FName write FName;
end;
TSQLDocument = class(TSQLRecord)
private
FBranch: TSQLBranch;
FName: RawUTF8;
published
property Branch: TSQLBranch Read FBranch write FBranch;
property Name: RawUTF8 read FName write FName;
end;
function CreateModel: TSQLModel;
implementation
uses
System.SysUtils,
mORMotDB, SynSQLite3;
function CreateModel: TSQLModel;
begin
Result := TSQLModel.Create([
TSQLAuthGroup, // Main
TSQLAuthUser, // Main
TSQLBranch, // References
TSQLDocument // Documents
], 'master')
end;
function DBFileName(const aFileName: String): TFileName;
begin
Result := IncludeTrailingPathDelimiter(ExeVersion.ProgramFilePath) + aFileName + '.db';
end;
constructor TMyRest.Create(AModel: TSQLModel);
function createProps(const aFileName: String): TSQLDBSQLite3ConnectionProperties;
begin
Result := TSQLDBSQLite3ConnectionProperties.Create(StringToUTF8(DBFileName(aFileName)), '', '', '');
with Result.MainSQLite3DB do
begin
Synchronous := smOff;
LockingMode := lmExclusive;
end;
end;
begin
FDocDBProps := createProps('Documents');
VirtualTableExternalRegister(AModel, TSQLDocument, FDocDBProps);
FRefDBProps := createProps('References');
VirtualTableExternalRegister(AModel, TSQLBranch, FRefDBProps);
inherited Create(AModel, DBFileName('Main'));
AModel.Owner := Self;
DB.LockingMode := lmExclusive;
DB.Synchronous := smOff;
CreateMissingTables;
end;
destructor TMyRest.Destroy;
begin
inherited Destroy;
FreeAndNil(FRefDBProps);
FreeAndNil(FDocDBProps);
end;
end.
Pages: 1