You are not logged in.
Ok, I found the reason... The linker searches the path: "/home/larand/fpc-library/mORMot2/static/x86_64-linux" but of some reason I got all files directly under the "static" folder.
I could have looked up a bit better.
Sorry I forgot to tell that I also added the static library. Here is what I got there:
larand@larand-VirtualBox:~$ ls fpc-library/mORMot2/static
crc32c64.o libdeflatepas.a quickjs.o sha512-x64sse4.o
dev.sha256 liblizard.a README.md sqlite3.o
larand@larand-VirtualBox:~$
Running Lazarus-3.6 on Ubuntu-20.04 using oracle vm.
I followed the instructions on GIT-ReadMe
"On Lazarus:
Just open and compile the /packages/lazarus/mormot2.lpk package;
and mormot2ui.lpk if needed.
"
And it executed without problem.
But when I tried to compile the example code I got the following messages:
Compile Project, Target: /home/larand/fpc-library/mORMot2/ex/http-server-files/exe/httpServerFiles: Exit code 1, Errors: 1, Warnings: 6
httpServerFiles.dpr(34,1) Warning: Library libdeflatepas.a not found, Linking may fail !
httpServerFiles.dpr(34,1) Warning: Object ../../static/x86_64-linux/sha512-x64sse4.o not found, Linking may fail !
httpServerFiles.dpr(34,1) Warning: Object ../../static/x86_64-linux/crc32c64.o not found, Linking may fail !
Warning: linker: /usr/bin/ld: cannot find ../../static/x86_64-linux/sha512-x64sse4.o: No such file or directory
Warning: linker: /usr/bin/ld: cannot find ../../static/x86_64-linux/crc32c64.o: No such file or directory
Warning: linker: /usr/bin/ld: cannot find libdeflatepas.a: No such file or directory
httpServerFiles.dpr(34,1) Error: Error while linking
It is probably something wrong with my setup of lazarus but I have no idea.
The reason for me to use ubuntu and lazarus is to create an application running on a PI-Zero2W having it to send messages over internet.
//LG
I try to convert the "mormotDDD" unit to be used by mORMot2 because I like to use "CQRS" the way it is used in the old mormot DDD-example.
It seemed to be an easy task from the beginning but I'm stopped by a few things.
The TClassInstance is not available in mORMot2. It look like "TRttiClass" could be used here but it is not defined the same.
The " property Aggregate: TClass read fAggregate.rttiClass ;" does not work as rttiClass is a function and not a field.
There are a few more things to fix but I think this is the most important to fix. The other may be solved easier when this is solved. Otherwise I'll come back and ask for more help.
I'll go for that, thanks ab.
I'm not sure I understand the last sentence. I can show you the last part of the jsonfile if that helps.
"DetaljReferens": [
{
"Referens": null,
"Referens_ReferensLista": "BSAB96:PR",
"Referens_ReferensKod": ""
},
{
"Referens": null,
"Referens_ReferensLista": "SBEF:Byggdel",
"Referens_ReferensKod": "90"
},
{
"Referens": null,
"Referens_ReferensLista": "PRODKOD",
"Referens_ReferensKod": ""
}
]
}
}
]
}
}
}
It has a fixed fields layout. I think it is created from a XML-file.
Just below 1MB > 24000 lines.
9 levels.
The json file I got is a very big one with lot of levels and would normally be split up into several tables but I have been asked to make one flat table of it.
To me it looks as very complicated task to solve so I would be happy to get some help to put me in the right direction.
I suppose that there are some very useful code in mormot. I've been looking a round a bit but it isn't that easy.
I have another version written for Lazarus if that could be easier for someone to checkout.
https://gitlab.com/GIT-Testing/mormot-r … th-lazarus
This version works as bad as any other of my attempts to find a working solution.
Looks that it is not possible to have the real-time sync with this library.
//LG
Please see my updated reply above.
It seems that it only works once after that I erase the user table first and then start the slave.
The data is copied from master to the slave just after the call:
fSlave.RecordVersionSynchronizeSlaveStart(TOrmUser, fMasterClient, nil);
But not after.
Very strange or...?
There is an error in the json structure
{
"c": "28403.81000000",
"a": "28420.61000000",
"A": "0.00351000", <--- THIS COMMA is not valid here.
},
You can use this link: https://jsonlint.com/
Best regards
//LG
Hello ab,
I've already checked out that code and I did a new attempt with a small testproject but still fail.
I have this project available as a zip-file but I don't find any way to share that. PasteBin doesn't seem to to be used with anything but sourcefiles.
Any idea how I can share this file?
I have the project on GITLAB if it could work?
https://gitlab.com/GIT-Testing/test_mor … cation.git
I made a small program that more or less copies the code you have in your test suite.
That code works but the difference is that I don't use services here. When simulating changes in the master table I do it directly through the TRestServerDB instance.
But in the real world I need to use services and then I need 2 different http-channels, one for update the table on the master and one for the replication.
And this case don't work for me. See the the GITLAB-project above.
I'm sure that I have misunderstood how to do this correct but I'm also sure it must be someone, that have a working solution of a similar system like mine, that could see what I've done wrong or..?
Otherwise I have to develop a compleatly different solution on my own :-(
Please help.
No answer in a week, I hoped to find some anwer here at least a comment if some necessary information is missing.
I tried to find a solution myself and I feel that I've done what is needed but in spit of that, no success.
I don't believe that this function is incomplete or not functional in mORMot2 but it looks so to me.
Only when the master server is restarted the sync works but only once.
In the slave log I can see the following repeated five times:
2023-10-05 09:24:14.640 Debug mormot.rest.sqlite3.TRestServerDB(035da1e0) RecordVersionSynchronizeSlaveStart(TORMUser) current=19 Subscribe(036ec488)
2023-10-05 09:24:14.640 Enter mormot.soa.client.TServiceFactoryClient(036fac98).InternalInvoke IServiceRecordVersion.Subscribe("User",19,1)
2023-10-05 09:24:14.640 Enter mormot.rest.http.client.TRestHttpClientWebsockets(03d93730).InternalUri POST
2023-10-05 09:24:14.640 Trace mormot.rest.http.client.TRestHttpClientWebsockets(03d93730) InternalRequest POST calling THttpClientWebSockets(03d939a0).Request
2023-10-05 09:24:14.640 Client mormot.rest.http.client.TRestHttpClientWebsockets(03d93730) POST DHSSync/ServiceRecordVersion.Subscribe status=200 len=18 state=1
2023-10-05 09:24:14.640 Leave 00.003.082
2023-10-05 09:24:14.640 Service return mormot.soa.client.TServiceFactoryClient(036fac98) {"result":[false]}
and then I get an error:
2023-10-05 09:24:14.704 Error mormot.rest.sqlite3.TRestServerDB(035da1e0) RecordVersionSynchronizeSlaveStart(TORMUser): retry failure
The code in the slave looks like this:
procedure TForm5.btnSynchronizeClick(Sender: TObject);
function setUpSyncServer: TRestServerDB;
begin
result := TSQLRestServerDB.Create(CreateSyncModel, ChangeFileExt(Executable.ProgramFileName, '.db'));
result.DB.Synchronous := smOff;
result.DB.LockingMode := lmNormal;
result.Server.CreateMissingTables;
end;
begin
if not fSyncRunning then
begin
btnSynchronize.Caption := 'Stopp Synk';
fSyncServer := SetUpSyncServer;
fHttpClientSync := TRestHttpClientWebSockets.Create('127.0.0.1', '8888', CreateSyncModel);
fSyncServer.RecordVersionSynchronizeSlaveStart(TORMUser, fHttpClientSync);
fSyncRunning := true;
end
else
begin
btnSynchronize.Caption := 'Synka';
fSyncServer.RecordVersionSynchronizeSlaveStop(TORMUser);
freeAndNil(fSyncServer);
freeAndNil(fHttpClientSync);
fSyncRunning := false;
end;
end;
The master part:
function setUpOrmSyncServer: TRestServerDB;
begin
result := TRestServerDB.Create(CreateSyncModel, ChangeFileExt(Executable.ProgramFileName, '.db'));
result.DB.LockingMode := lmNormal;
end;
begin
OrmSyncServer := setUpOrmSyncServer;
HttpSyncServer := TRestHttpServer.Create('8888', [OrmSyncServer], '+', UseBidirSocket,4 );
OrmSyncServer.RecordVersionSynchronizeMasterStart;
So I just wonder, What am I missing?
Ok, I've done that already but as the array can contain several hundreds of object in the worst case I thought that using a dynamic array could be more effective.
In this case, load and response time is not an issue but I like to be more effective and I can think of cases where this could be an issue.
And why not to be prepared?
I have a class that contains an array where I need to find a certain object in this array as I want to update this certain object.
I've tried to find examples of how to do this but still failed so my hope is that someone could have such an example to show how this could be done.
Thanks in advance!
I would like to inform the client when trying to add an already existing record.
But the exception from sqlite3 is not forwarded. The call IRestOrm.add just return zero and the exception can only be seen in the server log.
How could I solve this?
Thanks Thomas, excelent solution even though it didn't help eliminate the invalid pointer operation.
Thanks ab, your comment woke me up, I asked myself why I use the whole objects in the parameter list when I only need the ID:s.
So I replaced those parameters with the id:s of the objects and now everything is alright again.
I have two classes TArticle and TSubProject. A SubProject can contain several articles.
Articles can be added by demand to the subproject.
I'm using "ObjArrayAdd" to add new articles to the subproject.
Like: ObjArrayAdd(aSubP.Articles, aArticle);
But that's not allowed - I get this message from delphi: "Constant object can not be passed as var parameter"
So I tried to use an intermediate variable and that calms delphi but executing the program gives me another error:
"Invalid pointer operation" when the function exits. But the database is updated then.
I' understand that I do something really wrong but how should I do?
The repository:
function TDHSSyncRepository.addArticleToSubProject(var aSubP: TSubProject;
out aArticle: TArticle): TSyncRepoError;
var
s: TArticleObjArray;
begin
if RetrieveArticle(aArticle) <> srSuccess then begin
result := srNotFound;
exit;
end;
Result := srNotFound;
// --- --> ObjArrayAdd(aSubP.Articles, aArticle); <<--- "Constant object can not be passed as var parameter
s := aSubP.Articles; // <<---- Do this instead
ObjArrayAdd(s, aArticle); // << -- Works but gives "Invalid operation"
aSubP.Articles := s; //
end;
Best regards,
LarsG
Oh, I got your answer while I was writing.
Yea, I can see now!
Maybe I was not completely wrong about the parameter..?
I took the code from an example just to get a kickstart for testing.
I should be more careful.
Thank's a lot!
Sorry, I found the problem. It's of course my mistake. I misunderstood the usage of the parameter "ContractExpected". It looks like it should have the same name as the service without the leading 'T'. I got the impression of that this parameter is used to make it possible of same version control of the interface. So if you add a new or changed a method you could change this parameter and in that way you get a signal if you have different versions between client and server.
Strange is that I'm sure I had a different name for this parameter from the beginning without any problem.
Sorry I bothered you with this.
Best regards
Lars-G.
To make a simple test client I added the following code:
procedure TForm4.FormCreate(Sender: TObject);
begin
Model := TOrmModel.Create([],SYSTEMLocalROOT);
HttpClient := TRestHttpClient.Create(IP_Local, HTTP_PORT, Model);
HttpClient.ServiceDefine([IDHSLocalService], sicShared, DHS_LOCAL_CONTRACT);
HttpClient.Services[DHS_LOCAL_CONTRACT].Get(fDHS_LocalService);
end;
In the function "TServiceContainerClientAbstract" the parameter aInterfaces contains one interface "IDHSLocalService" which is correct here. I' only use one interface so far.
The call "fHash.FindOrNew(fHash.HashOne(@Item), @Item, nil);" with Item as 'DHS_LOCAL_CONTRACT' returns -161.
This lead to that he interface pointer will be nil.
I'm not sure how to reproduce with a RawUTF8 list of interface. Where should I put the call?
Suddenly my testclient crashed at start with an access violation in this call: "HttpClient.Services[DHS_LOCAL_CONTRACT].Get(fDHS_LocalService);"
Debugging showed that the function "fInterfaces.FindHashed(aUri)" returns -1.
I've just added a method to the interface, recompile both the server and client and then this happened. I also removed those changes but still the same problem.
Something else must have been changed but I can't imagine what it could be.
I need some idea of where to start looking.
I solved it by replacing IList with an ordinary dynamic array for the service. It requires some more coding but it works. I kept using IList in the repository though as I can initiate the collection in the service before calling the repository methods.
It seems to require too much effort to be worth solving ?
I'm very fond of IList but I'm trapped by the fact that I have to declare it as a constant in the parameter list on a service method.
So there is no way to initialize it.
I'm sure that there is a solution to this but at the moment I'm lost.
That looks good, I will soon test it.
Is there a way to have mormot create one index using two or more fields?
Beutifull! Thanks!
This is the result:
function TDHSSyncRepository.GetAllArticles(var aArts: TOrmArticles):TSyncRepoError;
var
ormArt: TOrmArticle;
begin
result := srNotFound;
FRestOrm.RetrieveIList(TOrmArticle, aArts);
if aArts.Count < 1 then
exit;
result := srSuccess;
end;
I get this error when the IList<TOrm> is zero referenced after using CreateAndFillPrepare.
// TOrmArticles is the same as IList<TArticle>
function TDHSSyncRepository.GetAllArticles(var aArts: TOrmArticles):TSyncRepoError;
var
ormArt: TOrmArticle;
i: integer;
begin
result := srNotFound;
i := 0;
// result := Collections.NewList<TOrmArticle>; //Tested doing this call outside/inside this function but same result.
ormArt := TOrmArticle.CreateAndFillPrepare(FRestOrm,'',[]);
while ormArt.FillOne do begin
aArts.Add(ormArt);
end;
result := srSuccess;
end;
When aArts no longer used I get the error.
What's wrong here?
Ok, I took a snap from my code and removed a lot of irrelevant code and changed the names in hope that it would be easier to read.
The "names" is just a packed record. I included it in the snap now.
Well, your suggestion worked perfectly.
I remembered when I saw it that I used it before but my memory is just a... memory now I'm afraid.
How should I deal with fields that sometimes contain NULL-values when using variants
The code below shows how I tried to deal with it.
You can't even check if the field is null before using it.
The only way I know is to have a
Try
except
around each assignment but that doesn't taste good.
I'm sure there is a better solution but I'm lost.
type
TNames = packed record
field1: integer;
theNullField: RawUTF8;
field3: RawUTF8;
end;
var
I: isqldbrows;
row: variant;
names: TNames;
begin
I := Props.execute('select * from dbo.mytable where name = ?', ['Peter']);
if I.Step(true) then begin
I.RowDocVariant(row,[]);
names.fImportDir := row.Field1;
if not row.theNullField. = null then // This raise an exception
names.theNullField := row.theNullField // This also raises an exception if executed.
else
names.theNullField := '';
names.Field3 := row.Field3;
I.ReleaseRows;
end;
Ok, that worked well, so many thanks.
I think there are a lot of different ways of doing this and it's hard to find out which method to use.
The project I'm working on now is a replacement of an existing function to make it much faster so the speed is the main purpose of this project.
The reason I want the data of the array stored as JSON is mainly to have better control when testing, perhaps when the application is tested and works well I might consider going back to blob-storage.
But it is still nice to have the possibility to check out the contents in the database.
Any ideas or suggestions are very welcome.
best regards
Lars
Ok, thanks a lot for the almost complete code.
But this one store the week array as a blob, how about having it stored as a JSON string? Wouldn't I need to register the array for JSON? And what more?
with best regards
Lars
Sorry, the JSON I wrote is not a real JSON array I only typed it in by hand into the database record to test my program.
I'm not sure about how a json string should look to correspond to my class so I'm not surprised that it's not correct.
A Week is a packed record with the name TWeek and then I declared the array TWeekArray from TWeek.
This Array is contained in a class: TWeeks and this class is the one I want to persist in a sqlite3-table. In fact, later I want to have it in an IN-Memory table but in the beginning, I will have it in a database during the test.
So the question is, how can I best persist this table, TWeeks, you have it at the beginning of this thread.
Forget about the JSON string.
Ok, the compiler is now satisfied but I still missing the data for the WeekArray.
The field "Week" in the db looks like : '{"currentYear": 2023,"ActualWeek": 8,"weeks": [{"ryear": 2023,"rindex": 1,"rweekno": 8,"rAvailableAmt": 186,"rRequiredAmt": 99}]}'.
It's a valid json string but I'm not sure if it is valid in this case?
Where can I find TRtti? I use System.rtti but that won't help.
Ok, I found it in mormot.core.rtti.
But I couldn't use TRtti but Rtti.
I have a class of TSynPersistent containing an array of records that I want to populate from a DB-table.
For the moment while testing, I use SQLite3.
I worked out a TOrm descendant used for reading the data.
Everything reads correctly except for the array which is nil.
I thought I didn't need to register but I now realized that I was wrong.
But I don't know how to do it. Following instructions from the documentation I tried this:
The record:
TWeek = packed record
rYear: TYear;
rIndex: TWeekBufferIndex;
rWeekNo: TWeekNo;
rAvailableAmt: TAvailableAmt;
rRequiredAmt: TRequiredAmt;
function getAvailableOnWeek: TAvailableOnWeek;
property availableOnWeek: TAvailableOnWeek read getAvailableOnWeek;
end;
TWeekArray = array of TWeek;
The class:
TWeeks = class(TSynpersistent)
private
fWeekArray : TWeekArray;
fActualWeek : TWeekNo;
public
function getWeek(index: integer): TWeek;
procedure SetWeek(const index: integer; aValue: TWeek);
published
property weekArray: TWeekArray read fWeekArray write fWeekArray;
end;
The registration:
initialization
var T: Pointer;
T := TypeInfo(TWeekArray);
TJSONSerializer.RegisterObjArrayForJSON(T,TWeekArray);
The TOrm descendant:
TOrmWeeks = class(TOrm)
private
fWeeks : TWeekArray;
fActualWeek : TWeekNo;
protected
public
constructor create;
published
property ActualWeek: TWeekNo read fActualWeek write fActualWeek;
property Weeks: TWeekArray read fWeeks write fWeeks;
end;
The problem is that the compiler doesn't like this: " TJSONSerializer.RegisterObjArrayForJSON(T,TWeekArray);"
It say's: [dcc32 Error] dddLOBTypes.pas(294): E2029 '(' expected but ')' found
and next error: [dcc32 Error] dddLOBTypes.pas(295): E2250 There is no overloaded version of 'RegisterObjArrayForJson' that can be called with these arguments
If I replace TWeekArray with a class the compiler will be satisfied.
So what do I missing here?
So there are no plans to make a new one then?
Is there an alternative to mORMot_1:s TDDRepositoryRestFactory.ComputeSQLRecord ?
It would be handy to have one, I've been searching for a while but have not found any.
Ok, that's good. As I'm new to MongoDB it also makes sense to start MongoDB with mORMot2.
When I tried to set up mORMot2 on Delphi and followed the instructions from the ReadMe file I got this code to work.
:-)
Completely new to MongoDB and tried one of your old examples:
https://blog.synopse.info/?post/2014/05 … ase-access
Installed the latest version (6.03) of MongoDB
and tried:
var Client: TMongoClient;
DB: TMongoDatabase;
serverTime: TDateTime;
res: variant; // we will return the command result as TDocVariant
errmsg: RawUTF8;
begin
Client := TMongoClient.Create('localhost',27017);
try
DB := Client.Database['mydb'];
writeln('Connecting to ',DB.Name); // will write 'mydb'
errmsg := DB.RunCommand('hostInfo',res); // run a command
if errmsg<>'' then
exit; // quit on any error
serverTime := res.system.currentTime; // direct conversion to TDateTime
writeln('Server time is ',DateTimeToStr(serverTime));
finally
Client.Free; // will release the DB instance
end;
end;
and gets: 'Unsupported OP_QUERY command: db.hostInfo. The client driver may require an upgrade. For more details see https://dochub.mongodb.org/core/legacy-opcode-removal'
Don't know if its possible use the latest version with mORMot1 any more or should I try with mORMot2?
Trying to find the best way to solve the problem with tables in multiple levels as the following example:
TSQLLevel0 = TSQLRecord
private
fName: RawUTF8;
published
property name: RawUTF8;
end;
TSQLLevel1 = TSQLRecord
private
fName: RawUTF8;
published
property L1Name: RawUTF8;
property lev0: TSQLLevel0 read fLev0 write fLev0;
end;
TSQLLevel2 = TSQLRecord
private
fName: RawUTF8;
published
property L2Name: RawUTF8;
property lev1: TSQLLevel1 read fLev1 write fLev1;
end;
Tried with CreateJoined but that could only be used on two levels only i.e. Level0 and level1.
Any suggestions would be appreciated.
I don't understand exactly what you meant but I did like this:
fStore: TSynConnectionDefinition;
pw: RawUTF8
pw := TSynConnectionDefinition.ComputePassword('myPassword');
fStore := TSynConnectionDefinition.CreateFromJSON(StringToUtf8('{ "Kind": "TOleDBMSSQL2012ConnectionProperties", '
+ '"ServerName": "MyServer\\SQLEXPRESS", "DatabaseName": "MyDB", "User": "username", "Password": "'+pw+'"}'));
fDBServer := TSQLRestExternalDBCreate(fModel, fStore, USE_AUTHORIZATION, []);
And that worked, but that wasn't exactly what you said?
It looked to be convenient to use "TSynConnectionDefinition" but I failed to make it work.
Login failed due to missing password.
My code look like this:
fStore: TSynConnectionDefinition;
fStore := TSynConnectionDefinition.CreateFromJSON(StringToUtf8('{ "Kind": "TOleDBMSSQL2012ConnectionProperties", '
+ '"ServerName": "MyServer\\SQLEXPRESS", "DatabaseName": "MyDB", "User": "username", "Password": "myPassword"}'));
fDBServer := TSQLRestExternalDBCreate(fModel, fStore, false, []);
TSQLRestExternalDBCreate looks for fStore.PasWordPlain which does not exist in fStore.
What's wrong with my assumption?
Is the following SQL realizable with the "TSQLRecord.createJoined" function or is there better alternatives?
I try to avoid running the SQL-script in the program due to fear of"SQL-Injection".
It's about an old legacy database. I have tested the function in simpler case where you use only one common field which work perfectly.
SELECT TE.AddressName, TC.Customername FROM tbl_A TA
join tbl_B TB ON TB.CustomerNo = TA.CustomerNo
join tbl_C TC ON TC.CustomerNo = TA.CustomerNo
join tbl_D TD ON TD.CustomerNo = TA.CustomerNo AND TD.AddressNo = TC.AddressNo
join tbl_E TE ON TE.AddressNo = TD.AddressNo
Sorry, I fixed it by deleting the table and then run the program again. But Yes I created the table with mORMot but I also have tested different Restservers and it might have been created different then.
I tested TSQLRestClientDB but could not understand how I should set it up. As I don't use SQLITE this time I could not put a filename into the third parameter, I did try but then it created a db3-file with some strange marks ('v') on the tables.
I have a small project here where I included everything in one program because it only is about updating a single file with a text field.
My problem is that when the server tries to add a record I'll get an error which says in short: "Cannot insert explicit value into ----- when IDENTITY_INSERT is set to OFF"
I'm running Delphi11 and MSSQL.
Links to part of the code: [URL https://1drv.ms/u/s!AkzgaKIeNifrhaQI3gC … A?e=SpWAz3 /url]
the error message: [URL https://1drv.ms/u/s!AkzgaKIeNifrhaQLwLN … w?e=02zydi /url]
Ok, thanks.
Yes, the problem was that I didn't use "TSynAutoCreateFields". After changing that, all is now well.
This happens when I use a class that have references to another class.
see below:
TLogType = class(TSynPersistent)
private
fName: RawUTF8;
fValue: integer;
published
property name: RawUTF8 read fName write fName;
property value: integer read fValue write fValue;
end;
TLogTypeObjArray = array of TLogtype;
TMyLog = class(TSynpersistent)
private
fLogdate: TDate;
fComment: RawUTF8;
fLogType: TLogType; <<<<-- the reference
published
property logDate: TDate read fLogDate write fLogDate;
property comment: RawUTF8 read fComment write fComment;
property logType: TLogType read fLogType write fLogType;
end;
The reference is flattened in the TSQLRecord so the field "LogType" will be two fields:
LogType_Name" and "LogType_value".
Don't have a clue any longer how to solve this. I have a small zip file of a test project to share but don't know where to put it.
The exception occurs in a section called "fake interface" in the mORMot unit.
Would appreciate any help to solve this. Also, a site where I could upload my little zip file (12k).
Maybe this link? https://1drv.ms/u/s!AkzgaKIeNifrhLQze1G … A?e=ZCbpMx
Thanks, that was much better - I've already tested and it works perfectly!