#1 mORMot 2 » Bug in TTextWriter.AddSpaced » 2024-03-27 13:36:40

sgavrilov
Replies: 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

#2 Re: mORMot 2 » RotateFileNoCompression » 2024-01-25 12:41:49

My fault! I had not pulled the latest version.

#3 Re: mORMot 2 » RotateFileNoCompression » 2024-01-25 07:40:54

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.

#5 mORMot 2 » RotateFileNoCompression » 2024-01-11 07:48:45

sgavrilov
Replies: 5

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

#6 Re: mORMot 2 » 'stored AS_UNIQUE' and OrmMapExternal » 2023-11-29 08:26:15

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

#8 Re: mORMot 2 » TClassNameToBeDeletedID does not work » 2023-11-28 09:21:20

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.

#9 mORMot 2 » 'stored AS_UNIQUE' and OrmMapExternal » 2023-11-14 08:00:03

sgavrilov
Replies: 1

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

#10 mORMot 2 » TClassNameToBeDeletedID does not work » 2023-11-14 06:29:23

sgavrilov
Replies: 3

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

#12 mORMot 2 » Proposal regarding IsValidUriRoute (mormot.net.server) » 2023-10-02 13:45:19

sgavrilov
Replies: 2

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

#13 Re: mORMot 2 » TRestHttpClientWinHTTP vs. TRestHttpClientWinSock » 2023-08-16 11:07:26

Thank you for the reply!

As I mentioned, I do not need bidirectional. So, I do actually have a choice. smile

As far as I can understand, the recommendations have been changed and the THttpClientWebSockets is the way to go now.

#14 mORMot 2 » TRestHttpClientWinHTTP vs. TRestHttpClientWinSock » 2023-08-16 07:46:15

sgavrilov
Replies: 5

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

#15 mORMot 2 » Error in OnlyChar » 2023-08-16 07:24:30

sgavrilov
Replies: 1

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

#16 mORMot 2 » mORMot2 migration table » 2023-05-29 14:04:03

sgavrilov
Replies: 0

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

#17 mORMot 1 » Nested objects in TSQLRecord for MongoDB » 2020-03-11 05:26:34

sgavrilov
Replies: 1

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

#18 Re: mORMot 1 » Splitting tables between several SQLite3 databases » 2019-05-26 17:07:18

Well, it does not work. sad But for a different reason. smile

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...

#19 Re: mORMot 1 » Splitting tables between several SQLite3 databases » 2019-05-25 12:01:34

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?

#20 mORMot 1 » Splitting tables between several SQLite3 databases » 2019-05-24 07:58:25

sgavrilov
Replies: 5

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.

Board footer

Powered by FluxBB