You are not logged in.
Hi Arnaud,
I found an errore in SynDB.pas : for MySQL , the sql returned for table and views contain the keyword concact instead of concat.
I'd also like if GetTableName and GetViewName function could take care of value stored in ForcedSchemaName : actually they always return tables for all schemas.
tx very much and a special greeting in this sad period.
Mario
Ok,
for other distracted as me : I have to use Iso8601ToTimePUTF8Char !!!
by
Sorry but , if I'm not wrong, Z indicates that the time value is the time in Greenwich, England, or UTC time.
What I was trying to say is that the function doesn't work with simple ISO 8601 time (without date).
Is there another way to handle this ?
Tx very much.
Hi AB,
if I try to convert an ISO8601 time expression (10:30:47.0Z), the function fails because doesn't start with an year (4 digits) but the standard allows this format.
No reply , so i opened a ticket...
Result : rejiected because it could break existing code.
I obviously agree wit this concept.
I don't agree with the fact that as is 'well' documented this stream is only for append. There isn't any line in the SAD saying that tRawByteStringStream may not be used as any other one derived from tstream class.
Hi AB,
sorry but i don't remember how to open a ticket.
The write methof of this object doesn't work well if one want to write before the end of the buffer, changing the stream position to do it.
function TRawByteStringStream.Write(const Buffer; Count: Integer): Longint;
begin
if Count<=0 then
Result := 0 else begin
Result := Count;
SetLength(fDataString,(fPosition+Result));
MoveFast(Buffer,PByteArray(fDataString)[fPosition],Result);
inc(FPosition,Result);
end;
end;
I have made this change and now seems to be right.
// before
SeltLength(fDataString,(fPosition + Result))
//after
if fPosition+Result > Length(fDataString) then SetLength(fDataString,(fPosition+Result));
Somebody sent me this : https://github.com/mariomoretti/GitHub/ … N_ANNO.mp4
Happy new year AB and everybody.
Mario
Ok,
I've modified as you suggested.
Tx very much
Hi,
I converted this rawutf8 dyn array :
['A'
'B'
''
'C'
'']
to csv with RawUTF8ArrayToCSV, obtaining A,B,,C, as result.
When I call CSVToRawUtf8DynArray , then returned array ignores the blanks element of the array and returns
['A'
'B'
'C']
I have modified the function in Syncommons , excluding the control on s>''. Now it seems to work well, returning the original array;
procedure CSVToRawUTF8DynArray(CSV: PUTF8Char; var Result: TRawUTF8DynArray;
Sep: AnsiChar; TrimItems: boolean);
var s: RawUTF8;
begin
while CSV<>nil do begin
s := GetNextItem(CSV,Sep);
if TrimItems then
s := trim(s);
//if s<>'' then begin
SetLength(Result,length(Result)+1);
Result[high(Result)] := s;
//end;
end;
end;
Tx AB,
now it works fine.
My problem was that, with default behavior, string values containing only digits inserted with asCSV, were returned by initfromcsv with a #13 added at the end.
1 was returned as 1#13.
tSynNameValue in syncommons has these two methods:
procedure InitFromCSV(CSV: PUTF8Char; NameValueSep: AnsiChar='=';
ItemSep: AnsiChar=#10);
and
function AsCSV(const KeySeparator: RawUtf8='=';
const ValueSeparator: RawUtf8=#13#10; const IgnoreKey: RawUTF8=''): RawUTF8;
are there reasons to maintain different default values for itemsep , or is it possible to uniform them ?
tx
TX AB,
First of all, what I do currently is not to inherit from TSQLRestServerDB, but use a TSQLRest field and fill it with a TSQLRestServerDB instance - sometimes using a TSQLRestServer field.
To separate logic and storage. You may use for instance a remote DB later on.Don't create an external SQLite3 database.
TSQLRestServerDB has already a Sqlite3 engine running, and its own database: no need to use SynDB here.
Sorry but I don't understand what you are telling me. I imagine how busy you are, but could you be so kind to show me an example ?
About transactions, the way to go is to use a TSQLRestBatch, not create manual transactions.
I can't use a tSQLRestBatch because sometimes , in the same transaction, i need to know the ID of an inserted record to fill a property of another class before inserting it (I've tried to define it as tSQLRecord, but in some circumstances i can't do it). If I have well undertstood the batchsend operates a commit, so, using batch, it's impossible to grant that all data are persisted togheter.
I've started a project where you can configure at startup if use a sqlite or an external database.
On server side , i'm using a tSQLRestServerDB to access the data , created using two distinct approach owing to the case :
fModel := CreateModel(aRoot);
fProps := nil;
if aDNS > '' then // ODBC
begin
fProps := TODBCConnectionProperties.Create(aDNS, '',aUser, aPwd);
VirtualTableExternalRegisterAll(fModel, fProps);
end
else fProps := TSQLDBSQLite3ConnectionProperties.Create(aDBFileName,'','','');
inherited create(fModel, aDBFileName);
if aDNS > '' then
begin
// optionally execute all external DB requests in a single thread
AcquireExecutionMode[execORMGet] := amBackgroundORMSharedThread;
AcquireExecutionMode[execORMWrite] := amBackgroundORMSharedThread;
end;
CreateMissingTables(0);
it seems to work , in the sense that the tables are created, updated etc... but , when i started to use an external database i've found some issues :
using server.transactionbegin and commit or rollback doesn't work : on external database transactions are not started.
mainengineretrieve is not called : i have found in mormot.pas : /// overridden methods for direct sqlite3 database engine call:
Obviously I need to override it using sqlite or external tables.
After days I've found a workaround to solve the first using fProp.Mainconnection.StartTransaction /commit/rollback for estrernal db and transactionbegin for sqlite.
But i don't know how to solve the second.
Is there anybody knowing a solution ?
tx very much
Sounds a very good reason.
I have added a control of StatementLastException after the createandfillprepare nested in transaction : now seems to work ok.
tx very much AB.
Well,
one thing i have clear , i am totally confused and thick as a brick !!
Sorry to annoying you with newby questions, but i'm trying to put order in my mind :
- a tSQLRestServerDb runs only on server side.
- I'm using a tSQLClientRestURI to run processes from remote.
- so , why a tSQLRestServerDB.CreateAndFillPrepare doesn't return an exception ?
Anhoter question please :
on server side , what object, instead of tMySQLRestServerDB, I should better use to access data by ORM?
Tx AB.
Hi AB,
in line 1337 of unit in subject there are these istructions .
except
on E: ESQLite3Exception do
GetAndPrepareStatementRelease(E);
end;
createandfillprepare , in case of exception, returns a null resultset without any advertisement.
This means that i have , every time , to check for tSqlRsetServerDB.StatementLastException to be sure that all worked fine ?
Now i'm only catching exceptions.
This is the trace of a fServer.TransactionBegin :
MormotSqlite3.TSQLRestServerDB.TransactionBegin
SynSqLite 3.tSQLDataBase.TransactionBegin
SynSqLite 3.tSQLDatabase.EXECUTE('BEGIN TRANSACTION')
SynSqLite 3.TSQLRequest.Execute
SynSqLite 3.tSQLRequest.Prepare
Sqlite3.prepare_v2....
I've tried to put breackpoints in syndbodbc.starttransaction , commit and rollback : they are never called by fserver.transactionbegin or commit.
I searched for examples without success : please help me.
tx
I'm sorry to return to this issue , but i'm accidentally seen that the problem persists.
This is how i define the connection to a postgres db via odbc on server side:
fProdConn := tODBCConnectionProperties.create(DNS, '',User, Pwd);
..... // add all table to model.
fModel := CreateModel(HTTPRoot);
VirtualTableExternalRegisterAll(fModel, fProdConn);
fServer := tSQLRestServerDB.create(fModel, databasename); // databse name is the sqlite3 db.
I'm updating db only on server side via the tSQLRestServerDB (clients uses only services via interfaces).
In this example i'm changing the row ID in same tables reding from Jobs the tableindex, oldID and newID.
Every change is immediately visible in db, and even if i change commit with rollback, the db doesn't revert to old state.
fServer.TransactionBegin(tSQLCD_Queue, 1);
try
Jobs := tSQLCD_Queue.CreateAndFillPrepare(fServer,'status=1 order by id', []);
try
while Jobs.FillOne do
begin
aSQLRecordClass := fServer.Model.Tables[Jobs.TIndex];
aSQLRecord := aSQLRecordClass.create(fServer, Jobs.OrigID);
try
// erase original record
fServer.Delete(aSQLRecordClass, Jobs.OrigID);
// Insert with new ID
aSQLRecord.IDValue := Jobs.NewID;
fServer.Add(aSQLRecord, true, true, False);
finally
aSQLRecord.Free;
end;
end;
finally
jobs.free;
end;
finally
fServer.commit(1, true);
// fServer.rollback(1);
end;
Please say me where i'm mistaking.
Tx very much.
Hi AB,
sorry for delay and tx very much.
Now it works, but i had to upgrade to last revision. Nothing of bad , except that now i'm encountering another problem in tSQLRestServerDB.Executelist .
This is the line that doesn't work no more on postgres DB via ODBC :
rows := DataBase.ExecuteList([tSQLCD_Params],'select data from ' + tSQLCD_Params.SQLTableName);
the error reported is : TODBCStatement.GetCol: "data" column had Indicator=1376
data is a blob and , debugging, i've found that the error is fired because of a modification in function GetDiagField of unit SynDbODBC, line 1848.
Before it was :
if ODBC.GetDiagRecW(SQL_HANDLE_STMT,StatementHandle,1,
Sqlstate,NativeError,MessageText,1024,StringLength) = 0 then
begin
i := 0;
while sqlstate[i]> #0 do
begin
result := result + sqlstate[i];
inc(i);
end;
end;
GetDiagRecW returned 0.
Now it is :
if ODBC.GetDiagFieldA(SQL_HANDLE_STMT,StatementHandle,1,SQL_DIAG_SQLSTATE,
@Status,sizeof(Status),StringLength)=0 then
SetString(result,PAnsiChar(@Status),StringLength) else
result := '';
The GetDiagFieldA doesn't return 0.
Hi to all,
tSQLRestServerDB.TableMaxID only works with Sqlite db?
In postgret it returns an error : rowid column doesn't exists.
I suppose that also "limit" could be a problem with some db engines.
tx
Hi,
i solved using transaction only with tsqlrestserverdb.transactionbegin : the complex query are needed only in select statements.
tx.
Ok,
sorry for delay.
i'd like very much to use a single Database.Db instance !
A little resume.
I started project using only sqlite3 and ORM.
When I switched to ODBC , I reailized that some queries were too complex for ORM , so I introduced the TSQLDBConnectionProperties (also for Sqlite, to have a unique way to access data).
This introduced transaction and locking problems owing to the way I created the connection.
Now , thank to you, i've understood how to do it for sqlite3.
Using an external db , via ODBC , i've attempted various ways, but I cannot resolve how to register the virtual tables before creating the ODBC connection.
Could you , please , tell me how to create the Database without creating , before, the ODBC connection on wich register the virtual tables ? I cannot solve how to do it (i always obtain exception because external engine is unknown).
tx very much
Hi AB,
i'm having trouble using a connection to access database (else sqlite or external).
Certainly i'm doing something of wrong, but cannot resolve the issue, so i've reproduced this little example in which I show how i'm defininig a TSQLDBSQLite3ConnectionProperties, used to access data when queries are too complex and a TSQLRestServerDB for ORM
Till when I don't fire a select (i.e. till when i don't use directly the connection) , all works fine, but, after the usage of a dbConn.execute, accessing data with ORM results in a unpredictable error : in this example , trying to update a blob field (only a blob) via ORM results in this error : Error SQLITE_BUSY (5) [Step] using 3.13.0 - database is locked, extended errcode=5.
Using ODBC also i've similar problems.
Coud you tell me how i should correctly define this scenario ?
tx very much.
unit Unit1;
interface
uses
{$ifdef MSWINDOWS}
Windows,
Messages,
Graphics,
{$endif}
Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls,
SynCommons,
SynDB,
mORMot,
mormotsqlite3,
synDBsqlite3;
type
{ TForm1 }
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
DBConn : tSQLDBConnectionProperties;
fConn : TSQLDBSQLite3Connection;
{ private declarations }
public
{ public declarations }
Database: TSQLRestServerDB;
Model: TSQLModel;
end;
TSQLSampleRecord = class(TSQLRecord)
private
fData : RawUtf8;
fBlobData : RAwByteString;
published
property Data : Rawutf8 read fData write fData;
property BlobData : RawByteString read fBlobData write fBlobData;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var Model : tSQLModel;
s : tSQLSampleRecord;
rows : isqldbrows;
V : Variant;
data : RawByteString;
begin
model := tSQLModel.Create([tSQLSampleRecord]);
DataBase := tSQLRestServerDB.Create(model,'test.db3');
DataBase.CreateMissingTables(0);
s := tSQLSampleRecord.Create(Database, 1);
s.Data := 'data';
s.BlobData := 'blobdata';
if s.ID = 0 then Database.Add(s, true);
s.Free;
dbConn := TSQLDBSQLite3ConnectionProperties.Create('test.db3','','','');
dbconn.MainConnection.Connect;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var Model : tSQLModel;
s : tSQLSampleRecord;
rows : isqldbrows;
V : Variant;
d : Rawutf8;
blobd : rawbytestring;
begin
// here the select (if not executed, updateblobfields works fine.
rows := dbconn.execute('select data from ' + tSQLSampleRecord.SQLTableName, [], @V);
if rows.Step then
d := rows.ColumnUTF8(0);
rows := nil;
s := tSQLSampleRecord.Create(Database, 1);
s.data := 'òaiosufvbnsliughjwrpt90rp35';
s.blobdata := 'òaifgdffosufvbnsliughjwrpt90rp35';
database.update(s);
database.UpdateBlobFields(s);
s.free;
database.Free;
model.free;
dbconn.Free;
end;
end.
You are right,i'm using two diverse connection, because i'm confusing direct sql via the connection and ORM.
I need to use direct sql because the use of queries too complex for ORM.
I'll try to use only one of the two method in a transaction.
tx very much.
Hi,
in my project one can decide to use sqlite or an external db via ODBC.
On server side, to uniform db access , I create a TSQLDBSQLite3ConnectionProperties or a TODBCConnectionProperties to access data and use theirs Mainconnection to start a transaction when needed.
if Params.ExternalDB then
fProdConn := TODBCConnectionProperties.Create(Params.DBName, '', Params.DBUser, Params.DBPwd)
else
fProdConn := TSQLDBSQLite3ConnectionProperties.Create(StringToUtf8(DataBaseName),'','','');
fProdconn.MainConnection.Connect;
fConn := fProdConn.MainConnection;
fConn is the connection used by the server to start a transaction.
The project initially used only sqlite as database, so there was no a connection, but the transactions where started via the TSQLRestServerDb.TransactionBegin, now it uses fConn.StartTransaction.
When installing , the program asks to define where host the tables, then creates and populates some of them with data , in a whole transaction : start a transaction, insert all rows and finally commit.
There are minus then one thousand rows to add and the inserting time was of few seconds using sqlite and TSQLRestServerDb.TransactionBegin.
Now , using fConn.StartTransaction , via ODBC take the same little time, but using sqlite , the inserting time results in more than two minutes and I've noted that it continuosly creates and deletes DatabaseName.journal on disk.
Probably there is something that i'm wronging , may someone help me ? Tx.
I'm using last revision of the framework.
Ok AB,
User may decide if use sqlite or odbc, so i've added a control to use the odbc connection or not and all now works fine.
Complex queries are already only on server side and almost all executed at startup.
tx very very much for you proverbial responsiveness.
Mario
Hello everybody,
I switched tables from sqlite to an external db via ODBC.
A statement worked fine in the sqlite engine , but now raise an error calling tSQLRestServer.InternalAdaptSQL.
The statement is (T is a tSQLTableJSON):
T := aRest.ExecuteList([tSQLCD_Company], 'select ID from ' + tSQLCD_Company.SQLTableName +
' where ownerco=0 or id in (select id from ' + tSQLCD_Company.SQLTableName +
' where ownerco=' + inttostr(maincompany.ID)+')');
maincompany.id is 0, so the resulting statement is
select ID from CD_Company where ownerco=0 or id in (select id from CD_Company where ownerco=0) (It seems not having a sense, but is only because the table is empty, so maincompany.id is 0 instead to be >0).
ownerco is a tID field.
The error is raised in TSynTableStatement.Create when processing the second where condition : id in (select id from CD_Company where Ownerco=0).
I've debugged and found that it seems dued to an unknown type for (select id from CD_Company where ownerco=0) , but i was not able to arrange it either patching the source or finding a workaround.
May anyone help me ?
Thanks in advance
Mario
tx very much.
Hi,
while debugging some routines i normally change a field value in a table using an external tool (sqlitespy), to put in it a value that force the code to do what i expect.
My code used a tSQLRestServerDB to access the database , but now i'd like to use interfaced services.
If I use only ORM methods that access the database by the server instance, I may change the data from sqlitespy, but when i start e client session using tSQLHttpCLient, i cannot more do it. The program returns this error : Sqlite error 5 - database is locked.
I've posted the code. The lock happens in the line fClient.SetUser('User', 'synopse').
I have submitted this post only to understand if this is normal (the framework works without problems) or if this can be dued to some of wrong done defining server or client or so on.
Thanks in advance to anyone.
if fClient = nil then
begin
fClientModel := tSQLModel.create(servizi.Server.Model); // clone the server model
tout := params.HTTPTimeout * 1000;
fClient := tSQLHTTPClient.Create('localhost', Params.httpport, fClientModel, tout, tout, tout);
if not fClient.ServerTimeStampSynchronize then
begin
result := nil;
ShowMessage(UTF8ToString(fClient.LastErrorMessage));
exit;
end;
fClient.SetUser('User', 'synopse');
fClient.ServiceDefine([ICDAnagraphics], sicClientDriven);
fClient.OnAuthentificationFailed := RetryAfterTimeout;
end;
bonjour ab,
problably i'm trying to do something of wrong (as often i do), but i'd like your opinion.
i've defined an ancestor class derived from tSQLRecord : tClass1.
This class is a common ancestor for other classes : tAClass1, tBClass1 etc.
In a unit i've defined some common tasks on tClass1.
I pass to this unit instances of tAClass1, tBClass1 as a parameter , fData (obviously defined of type tClass1) and the exact derived class type in an other parameter fDataClass.
All works fine except that when i do a copy of such instance in this way : fDataCopy := fData.CreateCopy as fDataClass. On destroying it, i obtain a memory leak.
My question is : i cannot use createcopy in this a way and must define methods on each derived class, using the exact class (instead of fData of type tClass1), or the record destructor should anyway release correctly the data ?
tx
ok ab,
as we know , the documentation of this big framework is BIG.
What about raise an axception on b.A=A.AstSqlRecord if A.ID is greater than maxint? It could avoid any misunderstanding (now , in the db field, is stored 2147483648!).
I may agree about little search but... i haven't done any search , thinking that record properties should permits to store any record.
Sorry.
I've two classes
ClassA = class(tSqlRecord)
protected
fdesA : rawutf8;
published
property desA : rawutf8 read fDes write fDes;
end;
ClassB = class(tSqlRecord)
protected
fdesB : rawutf8;
fA : ClassA;
published
property desB : rawutf8 read fDesB write fDesB;
property A : ClassA read fA write fA;
end;
In class a there is an instance with ID = maxint + 1 (2147483648).
procedure test;
var a : classa;
b : classb;
aID : tID;
begin
a := classa.create;
a.desa := 'A';
a.idvalue := 2147483648;
server.add(A, true, true);
b := classb.create;
b.desb := 'B';
b.A := A.Astsqlrecord;
aID := server.add(b, true);
b.free;
b := classb.create(server, aID);
aID := b.A.ID; // here an access violation error is returned.
b.free;
a.free;
end;
Debugging i've seen that in mormot.pas the assigment of value to property b.A is done using TSQLPropInfoRTTIInt32 class instead of TSQLPropInfoRTTIInt64.
ok,
I'll redefine creation methods.
tx
Hi AB,
we may agree on fact that audit trail is an half DELorean : it permits to move only to the past.
In some situations i need to determine the future.
So i've thought to do a new class with
an array storing only properties to be changed , one row for each future date.
an EndOfValidityDate , storing the last date of validity of properties actually stored in properties.
a StartOfValidityDate (optional)
methods to manage them
obviously i need a OnAfterRead event on server side , to verify EndOfValidityDate and , if the case, apply pending changes. It could be fired only for classes with audit trail enabled or, also, be independent from it, but associated to the class, to avoid unneeded calls.
I've looked , without success, for it. Can you help me , please ?
tx
No, httpserver threads end normally : remain only other unnamed threads.
Im investigating....
yes but, the unnamend threads end after one minute, non so do the httpserver worker that stay alive forever : i haven't killed the last process and they are still running.
After a while, the unnamed threads end.
In thread status panel stay these threads in state runnable (while destroying form they are in statu stopped)
Main thread :
tSQLHttpServer worker : 32 threads.
sometimes there are other 3 unnnamed threads (only id).
objects destroy order is :
Client
HttpServer
Server
Hi AB,
first of all : finally my interfaced based service works ! tx very much !!!
The server application has a main form wich ownes a tSQLRestServerDB an also a tSQLRestClientURI, created only on need : this to permit some administration customization.
All works fine but, shutting down the process , if I've created and used the interface methods from client, some threads remain active after all objects have been destroyed : consuming over a 30% of cpu resources.
When starting the program , without using the inside client, shutdown works well.
I'm in this situation because initially, the application used vcl objects and direct calls to the server : then , adding services and permit use of your great tSQLGTableToGrid, I've introduced the client.
I have to create two distinct program ? The server and a client for administration tasks ?
tx
indeed was very simple! tx
AB,
i'm confused.
i've asked you to fix my sample and not to refer to documents i've already read many an many times...
Hi AB,
sorry but i can't retrieve the data.
After a long period i'm yet a beginner, so, i've used a sample project : Sample 02 -Embedded Sqlite3 ORM with these changes :
in Sampledata :
TSQLSampleRecord = class(TSQLRecord)
private
fQuestion: RawUTF8;
fName: RawUTF8;
fTime: TModTime;
fOwner : tSQLSampleRecord;
published
property Time: TModTime read fTime write fTime;
property Name: RawUTF8 read fName write fName;
property Question: RawUTF8 read fQuestion write fQuestion;
property Owner : tSQLSampleRecord read fOwner write fOwner;
end;
In Unit1 :
procedure TForm1.AddButtonClick(Sender: TObject);
var Rec [ins], Rec1[/ins]: TSQLSampleRecord;
[ins] Ok : Boolean;[/ins]
begin
Rec := TSQLSampleRecord.Create;
try
// we use explicit StringToUTF8() for conversion below
// a real application should use TLanguageFile.StringToUTF8() in mORMoti18n
Rec.Name := StringToUTF8(NameEdit.Text);
Rec.Question := StringToUTF8(QuestionMemo.Text);
if Database.Add(Rec,true)=0 then
ShowMessage('Error adding the data') else begin
Rec.Owner := Rec;
Database.Update(Rec, 'Owner');
Rec.Free;
Rec := tSQLSampleRecord.Create(Database, 1);
Rec1 := tSQLSampleRecord.CreateAndFillPrepare(Database, 'Owner=?',[Rec]);
Ok := Rec1.Fillone;
NameEdit.Text := '';
QuestionMemo.Text := '';
NameEdit.SetFocus;
end;
finally
Rec.Free;
end;
end;
Well : this are fields values after Database.Update :
ID = 1 , Owner = 33247792 : I think this is normal owing to refrerences and so on…..but , deleting database and reexecuting the code , Owner's value changes everytime, also inserting same values in name and question (???).
However : Fillone returns Ok = False.
The Sql generated by ORM is :
Select ID, Time, Name, Question, Owner from SampeRecord where Owner=:(''tSQLSampleRecord''):
So I've changed query with
Rec1 := tSQLSampleRecord.CreateAndFillPrepare(Database, 'Owner=?',[Rec.ID]);
and deleted database.
Now ID=1 and Owner = 35869232
Fillone returns OK = False.
The Sql generated by ORM is :
Select ID, Time, Name, Question, Owner from SampeRecord where Owner=:(''1''):
AB, please, don't say me to read you huge documentation or posts : at this point we can assume that I'm not able to understand them !
My you correct the lines i've changed to let them to work as expected ?
Tx very much.
I've two tsqlrecord classes : the second having the first as property.
class1 = class(tsqlrecord)
protected
fDescription : rawutf8;
published
property Description : Rawutf8 read fDescription write fDescription;
end;
class2 = class(tsqlrecord)
protected
fDescription : rawutf8;
fOwner : class1;
published
property Description : Rawutf8 read fDescription write fDescription;
property Owner : class1 read fOwner write fOwner;
end;
class 1 contains some owners.
class 2 contains some children for every owner.
Now i know a class1 instance (Pippo) and i want to display in a grid all class2 children whose Owner is Pippo.
If I code : (aRest is a rest client .., aTable a SQLTable ecc. .)
aWhere := 'Owner=' + inttostr(Pippo.ID);
aTable := aRest.List([Class2],
'Description',
aWhere);
aTable (as I expected ) , is empty.
I've change filter in these ways :
aWhere := 'Owner=' + inttostr(Pippo.recordreference(aRest.model));
aTable is empty.
aWhere := 'OwnerID=' + inttostr(Pippo.ID);
catch an sqlite exception. So do :
aWhere := 'Owner.ID=' + inttostr(Pippo.ID);
Please , may you help me to do this SIMPLE query ?
Tx very much.
Mario
Hy Ab,
you are right !
I hope it's due to this oppressive heat and not to age....
Tx
Mario
Hy Arnaud,
After a while....
I've changed in mormot tSQLRestClient.BeforeUpdateEvent (and related calls and update methods to eliminate const from CustomFields)
from
function BeforeUpdateEvent(Value: TSQLRecord): Boolean; virtual;
to
function BeforeUpdateEvent(Value: TSQLRecord; Var CustomFields : tSQLFieldBits): Boolean; virtual;
then i have overriden the method in my own tSQLRestClient :
function TMySQLRestClient.BeforeUpdateEvent(Value: TSQLRecord; Var CustomFields : tSQLFieldBits): Boolean;
var I : integer;
Empty : Boolean;
OldData : tSQLRecord;
ChgFields : tSQLFieldBits;
begin
OldData := Value.RecordClass.Create(Self, Value);
try
ChgFields := CustomFields;
Empty := IsZero(CustomFields);
CustomFields := [];
Result := false;
with Value.RecordProps do
for I := 0 to high(SimpleFields) do
// compare not TSQLRawBlob/TSQLRecordMany fields
with SimpleFields[ I] do
if Empty or (I in ChgFields) then
if CompareValue(Value, OldData, false)<>0 then
begin
include(customfields, I);
result := true;
end;
finally
OldData.free;
end;
end;
Obviously, could be implemented on clients but so, i don't lock data from clients when reading first and save some bandwidth when reading data before update.
Do you think that could be implemented in mormot ?
Thanks.
regards
Mario.
tx a lot AB !!!