You are not logged in.
it seems that the concern is about 1.15 version.
I have some older version (from may 2011, don't know which revision it was) on my HDD which compiles properly.
Hi,
I need to compile the runtime package (plugin for my application) which uses SynCommons, however - while linking, I get this error: Need imported data reference ($G) to access 'VarCopyProc' from unit 'SynCommons'.
What however is *really* strange is that the plugin works properly (it's supposed to generate PDF report, maybe the pdf-related code does not use the 'problematic' part of code?)
I've tried of course including {$G+} and {$IMPORTEDDATA ON} in Synopse.inc but it didn't solved the problem.
well I'm not sure how about FastCGI specs, but on my linux-running http server, FastCGI is configured with MAX number of threads to spawn. However when server starts it spawns several threads, and - as request number raises - it spawns new threads, until number reaches max.
Just transfer the content as RawUTF8.
And use the corresponding functions of SQLite3Commons.
thank you.
I didn't expected it to be SO trivial
Hi,
since object serialization to/from JSON is used internally I wonder what'd be the easiest way to send serialized objects between Client / Server using REST callbacks?
Thanks,
mg
All server threads are created at once, at HTTP server initialization.
I thought threads creation depends on number of requests (like FastCGI)
I've updated the documentation to explicitly states this requirement:
An important point is to remember that the implementation of the callback method must be thread-safe. In fact, the TSQLRestServer. URI method expects such callbacks to handle the thread-safety on their side. It's perhaps some more work to handle a critical section in the implementation, but, in practice, it's the best way to achieve performance and scalability: the resource locking can be made at the tiniest code level.
thank you
Is there some possibility to have my Service notified when new thread is shipped?
Hi,
if I understood the code & docs properly, the instance of TSQLRestServer I'm exposing through TSQLite3HttpServer might be used in context of several threads (is thread pooling implemented for this?). If I'm correct, do I have to make sure TSQLRestServer callbacks are thread-safe, or are they synchronized by http thread?
I've modified TSQLite3HttpServer.Create to use one more parameter : const AServerInstance: THttpServerGeneric = nil.
see this (modified) part of code
{$ifndef USETCPPREFIX}
try
// first try to use fastest http.sys
if AServerInstance = nil then
fHttpServer := THttpApiServer.Create(false)
else
fHttpServer:= AServerInstance;
if fHttpServer.InheritsFrom(THttpApiServer) then
for i := 0 to high(aServers) do begin
and here is "my" server:
TmyServer = class(THttpApiServer)
protected
destructor Destroy; override;
procedure Execute; override;
end;
procedure TMyServer.Execute;
begin
CoInitialize(nil);
inherited;
end;
destructor TmyServer.Destroy;
begin
CoUninitialize();
inherited;
end;
the above example works on the first request.
Now if I change the parent class to THttpServer (so it won't use kernel.sys) the example does not work at all.
Even though the CoInitialize is called (I've debugged it), when calling CoCreateObject in request it fails saying CoInitialize wasn't called ;]
any ideas here?
I've built a trivial sample to prove that it's not about threads:
and, yes, it works. each call to GetValue works as expected.
type
ttestthread = class(TThread)
private
fClient: IeClient;
protected
destructor Destroy; override;
procedure Execute; override;
end;
procedure ttestthread.Execute;
var s: string;
begin
CoInitialize(nil);
fClient.Init;
while true do
begin
Sleep(1000);
fClient.GetValue('Random', s);
s:= s + 'abc';
end;
end;
destructor ttestthread.Destroy;
begin
CoUninitialize();
inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
client: IeClient;
t: ttestthread;
begin
client:= Te.Create;
if not client.Init() then
raise Exception.Create('Could not init Client!');
t:=ttestthread.Create(true);
t.fClient:= client;
t.Resume;
end;
any ideas what are differences between this and THttpServer?
some context management? Thread pooling?
Hi,
first of all it seems the call to CoInitialize need to be done in Execute; method.
second of all, for some reason it need to be called for each request - otherwise I get the same error as previous on the second request (even though CoUninitialized is called in thread Destructor.)
are there any low-level tricks behind handling Request, which may cause this behavior?
once again,
1. CoInitialize is called at the very beginning of Execute; method
2. CoUninitialize is called in Destructor
3. calling CoCreateObject in the registered REST Server method works only for the first time - second call throws exception saying "CoInitialize was not called"
4. it works if I move CoInitialize to REST Server method...
any ideas?
Wow, that is pretty impressive!
I'm glad that, once you gave up using ZEOS and chosen OleDB instead, there's still possibility to introduce direct, native drivers like that one
Hope to see others, including MySQL for example
The HTTP server has its own thread(s) so you'll need to call CoInitialize for each thread.
You may do this by overriding the THttpApiServer.Create constructor (if you use THttpApiServer for the HTTP server class). Without forgetting to call CoUninitialize in an overriden constructor.
I'm using TSQLite3HttpServer which, as I can see, tries to use THttpApiServer if possible, if not - it falls back to the higher-level ones.
That would require me not only to override methods in one class (HttpApiServer) but also the others (just in case the first one won't be used).
Not to mention the need to modify TSQLite3HttpServer in order to use "my" classes.
I wonder if there's any better "place" (method) to hook in thread creation?
Hi,
I've extended your "06 - Remote JSON REST Service" example, so that server-side calls some external libraries using COM in order to return value when requested.
Simply, I need to expose results of some local COM calls over HTTP.
Thus I modified your example to use TSQLite3HttpServer and TSQLite3HttpClient instead of Named Pipes.
Now the function which uses COM is called in TServerForm.Button1Click (yeah, I've added a Form to the server).
Now when I run the server and click it's Button1, it works as expected - obtains the values from COM call.
Unfortunately, if I call ServerForm.Button1Click from my Service (descendant of TSQLRestServer) it fails, saying CoInitialize was not called.
Is that kind of thread/context problem?
It is really weird, once again - if I click the button itself, it works. If I run the client which - over HTTP - requests the server to call the OnClick code, it fails on CreateComObject saying "CoInitialize" was not called. If I click the button itself again, it works fine again.
Are ActiveX and ComObj units part of PE version?
yes, both are available.
You should certainly generate RawUTF8 instead of UTF8String, for proper UTF-8 handling.
indeed, my bad.
I'm thinking also of a similar generator, but for reverse engenering of a Database instead: from an existing DB, in whatever format (thanks to OleDB drivers of our mORMot), some mapping classes will be generated.
well, I guess you're quite busy developing mORMot, maybe I could take care of creating such a basic tool?
Yet, I don't think current version of SynOleDB is able to handle such a thing
// BTW, SynOleDB uses OleDB which breaks the compatibility with D7PE, since OleDB is not a part of PE version
Hi,
since I'm using Delphi 7 Personal which has no tools for class autocompletion, I had to write my own pseudo-meta-language tool.
It simply works by specifying fields one by one like following:
name:str
age:int
weight:float
the result is complete class declaration code, including class name, private variables and published properties.
additionally each field might be marked with ";r" and/or ";w" flags which will cause the generator to use getter and/or setter respectively for specific field.
for example
Name:str
Surname:str
Phone:str;r;w
Age:int;r
Weight:float
results in
TSQLPerson = class(TSQLRecord)
private
fName: UTF8String;
fSurname: UTF8String;
fPhone: UTF8String;
fAge: integer;
fWeight: float;
function GetPhone():UTF8String;
procedure SetPhone(const AValue: UTF8String);
function GetAge():integer;
published
property Name: UTF8String read fName write fName;
property Surname: UTF8String read fSurname write fSurname;
property Phone: UTF8String read GetPhone write SetPhone;
property Age: integer read GetAge write fAge;
property Weight: float read fWeight write fWeight;
end;
function TSQLPerson.GetPhone():UTF8String;
begin
result:= fPhone;
end;
procedure TSQLPerson.SetPhone(const AValue: UTF8String);
begin
fPhone:= AValue;
end;
function TSQLPerson.GetAge():integer;
begin
result:= fAge;
end;
the tool has also ability to parse back from Delphi class declaration to meta-language, but it'll work correctly only if the code follows the pattern used for code generation (private variable: fFieldName, setters / getters: Get/SetFieldName etc etc)
Here's the link (binary + source):
I understand, thank you
Hi,
I'm very glad to see the latest Fossil version to use TMS optionally.
The code is mentioned to be 1.13 release, however I'm not able to found 'official' release - just the Fossil leaves - which don't contain sqlite3.obj ... could you please tell me where to download official release?
Thanks,
m
huh ... what log?
Hi,
I've modified the demo so it is compatible with current (as for 4 May 2011) sources from Fossil.
BTW where's the official release 1.13 available?
thanks, I missed it somehow ... sorry.
You're trying to reinvent the wheel...
indeed. that's because I needed to implement very simple TSQLRibbon equivalent, but TMS-independent.
Removing TMS dependencies from UIEdit form was fast and easy, so I decided to create very simple general editor with grid preview on my own ... anyway, thanks for the tips!
FillMany works on pivot tables, while I'm talking about "direct" relations, so I don't see how is it supposed to work?
Thank you I'll update the source and dig into it tomorrow
Arnaud, possibly it's the same problem I had when dealing with empty TGDIPages.
I wanted to create instance of TGDIPages on application startup, then - later - fill it with data.
creating empty TGDIPages and assigning a parent caused List index out of bounds(0) error.
I had no time for tracking the issue so I simply added those two lines
fReport.BeginDoc;
fReport.EndDoc;
but I guess it might be the same problem as Martin had.
some thoughts ...
* FillOne -> at first, free all "related" objects (if they were created with FillOne)
* there should be a list of objects to free, so we won't confuse them with manually assigned objects.
* FillOne loads initializes all the 'related' fields with their instances
* each initialized instance is added to 'to-remove' list.
* there should be some 'guard' in order to protect from loading circual references.
If you put a _ before the property name, like _FullName, it won't appear in the UIEditor.
Thanks, but
1. appears in the Grid (TSQLTableToGrid)
2. uses additional memory in the database (which is not that important, but still...)
3. I guess it's slower than ID-based record identification
but for a quick fix it's pretty good
We'll need to have some other custom display of record if there is no unique field: like Name+' '+FirstName or such...
Yes, that's what I did as for now - I did following:
function GetFullName: RawUTF8;
published
property FullName: RawUTF8 read GetFullName stored false;
/// ....
function TSQLPerson.GetFullName: RawUTF8;
begin
result:= fSurname + ', ' + fName;
end;
which actually works, but is tricky - creates completely unnecessary field in SQL, also displays the field in UIEditor.
If I don't define my own unique field, ID is default one, right?
So, how about using ID as unique field for internal operations (storing it in ComboBox.Items.Objects casted to TObject) while for displaying create virtual method in TSQLRecord, called GetUniqueDisplayName - which could be implemented by user, or - if it's not implemented - just use first sftUTF8Text field?
it seems that the new code fixed that bug Thank you !!
Now the only thing left here is the record-not-being-unlocked bug
role:= TSQLUserRole.Create(globalClient, fRoles.Dest.ID);
but in the last case, the ID value must be up to 1,048,576 (i.e. $100000).
from what I investigated the trick, it just changes the way it's written from PtrInt(fRoles.Dest) to fRoles.Dest.ID.
But still I have no direct access to the "real" Dest object, I need to create & later free it manually.
Let's say I have a TSQLLesson which has property Teacher: TSQLPerson.
to speed up development Id like to write code like this:
var
l: TSQLLesson;
begin
l:= TSQLLesson.Create(db, fLessonID);
ShowMessage(l.Teacher.Name);
correct me if I'm wrong but currently framework doesn't support it, I need to declare TSQLLesson.GetTeacher
which returns
function TSQLLesson.GetTeacher: TSQLPerson;
begin
result:= TSQLPerson.Create(db, Teacher.ID); // or PtrInt(Teacher);
and of course handling memory free manually.
Do you think it'd be possible to automate the object retrieval & freeing that way?
investigating the problem, I found out that it is something about "GetJSONObjectAsSQL", part called "if Update" in "Return" subroutine.
Changing that part to much slower but definitely "safe" code (see below) I've elminated the error described above ... but now there's a problem with freeing TSQLRestClientURI object. FastMM detects operation on freed object on shutdown.
See the stack trace to the error...
BTW commenting out the FreeAndNil(dbConnection) part results in memory leak but no errors ... seems like there's some "after-update" operation pending, finally called on finalization...
if Update then begin
// returns 'COL1="VAL1", COL2=VAL2' (UPDATE SET format)
result:= '';
for F := 1 to FieldsCount do begin
result:= result + Fields^;
if InlinedParams then begin
result:= result + '=:(';
end else begin
result:= result + '=';
end;
result:= result + Values^;
if InlinedParams then begin
result:= result + '):,';
end else begin
result:= result + ',';
end;
inc(Fields);
inc(Values);
end;
result[length(result)]:= ' ';
well that's all I found, I hope it helps you somehow
//////
further investigating
I found out that each edited record doesn't get unlocked right after edition. It's ID stays on the Model's list of Locked IDs. That is why the "calling operation on freed object" error occurs.
But also, because of this bug, I'm not able to edit same record twice during the application runtime.
stored false;
there's so much I have to learn about RTTI
yeah, indeed making that field unique is not the best idea ... but current way of handling automated relation edition makes it practically useless
MultiByteToWideChar method solved the problem Thank you!
Delphi7 ... Regional characters - ą ę ś ć ź ł ń ó ż
GetACP returns 1250.
TextAlign := taLeft; just before printing the sting.
Ok, I have changed the declaration to RawUTF8 on all the fields.
I'm using Delphi 7. And the problem still occurs...
actually the problem is reproducible on my sqlite-framework-demo which as you know doesn't require TMS.
ok, so I've reverted all the changes back to your version. Than declaret two records:
TSQLCourse = class(TSQLRecord)
private
//fields declaration here
published
property Instructor: TSQLPerson read fInstructor write fInstructor;
property Person: TSQLPerson read wPerson write fPerson;
and
TSQLPerson = class(TSQLRecord)
private
fName: RawUTF8;
fSurname: RawUTF8;
fPhone: RawUTF8;
published
property Name: RawUTF8 read fName write fName;
property Surname: RawUTF8 read fSurname write fSurname;
property Phone: RawUTF8 read fPhone write fPhone;
end;
I have some "person" records.
Now when I call TRecordEditForm on TSQLCourse, both comboboxes are empty ...
//debugging the model creation, I found out that in my case the P^.StoredProc never equals to integer(false), so the "Unique" flag is never being set to true ... thus fMainFieldName[false,x] is never filled with data.
// it seems that P^.StoredProc always equals to decimal number 1. At least for all the records I debugged it, including MainDemo records.
Hi,
I'm playing with the UIEdit, and I tried editing the record which has related fields ...
first of all I was quite amazed that the related fields were of combobox type
But all the combos were empty. Debugging and investigating I found out that fMainFieldName contains empty string for each model.
Following that path I changed SQLIte3Commons:9068 from "sftUTF8Text: begin" to "sftUTF8Text, sftAnsiText: begin"
(I haveall the fields declared as UTF8String, BTW ... but the type was always sftAnsiText).
so it now fills "non-unique" array of names ... but still the "unique" part is empty .. what am I doing wrong?
Hi,
it seems that most recent leaf (downloaded from Fossil today) has some memory corruption problem while calling "update" method.
here's the most simple example of application (sources) which throws AV on application closing, if "update" was called.
http://migajek.us.to/tmp/synopsesqlite-update-bug.zip
in my more complex case I get following error report (FastMM):
FastMM has detected an error during a FreeMem operation. The block footer has been corrupted.
The block size is: 84
This block was allocated by thread 0xFCC, and the stack trace (return addresses) at the time was:
402A03 [System][@GetMem]
404959 [System][@NewAnsiString]
404F1D [System][@LStrSetLength]
4AB2EE [SQLite3Commons.pas][SQLite3Commons][Return][6387]
4AB5AD [SQLite3Commons.pas][SQLite3Commons][GetJSONObjectAsSQL][6462]
4AB6BD [SQLite3Commons.pas][SQLite3Commons][GetJSONObjectAsSQL][6480]
5611A5 [SQLite3.pas][SQLite3][TSQLRestServerDB.EngineUpdate][3634]
4B1BF8 [SQLite3Commons.pas][SQLite3Commons][TSQLRestServer.URI][11031]
5618A7 [SQLite3.pas][SQLite3][TSQLRestClientDB.URI][3870]
4B0221 [SQLite3Commons.pas][SQLite3Commons][TSQLRestClientURI.Update][10161]
5B2CEC [frmRecordListing.pas][frmRecordListing][TFrame2.EditRecord][92]
The block is currently used for an object of class: Unknown
The allocation number is: 4261
The current thread ID is 0xFCC, and the stack trace (return addresses) leading to this error is:
402A23 [System][@FreeMem]
4048CE [System][@LStrArrayClr]
5611F7 [SQLite3.pas][SQLite3][TSQLRestServerDB.EngineUpdate][3637]
4B1BF8 [SQLite3Commons.pas][SQLite3Commons][TSQLRestServer.URI][11031]
5618A7 [SQLite3.pas][SQLite3][TSQLRestClientDB.URI][3870]
4B0221 [SQLite3Commons.pas][SQLite3Commons][TSQLRestClientURI.Update][10161]
5B2CEC [frmRecordListing.pas][frmRecordListing][TFrame2.EditRecord][92]
5B2E4B [frmRecordListing.pas][frmRecordListing][TFrame2.btnEditClick][160]
4630DE [Controls][TControl.Click]
45A76D [StdCtrls][TButton.Click]
45A861 [StdCtrls][TButton.CNCommand]
followed by 'invalid pointer operation'.
Finally, the debugger shows following line
OK := EngineUpdate(Table,ID,SentData);
SQLite3Commons.pas:11031
Hi,
I have a code as following:
private
fRec: TSQLRecord;
// ...
begin
fRec:= TSQLPerson.CreateAndFillPrepare(dbClient, '');
fGridAdapter:= TSQLTableToGrid.Create(DrawGrid1, fRec.FillTable , dbClient);
now the problem is I can't free "fRec" on form destroy, because it also frees it's table - which is later freed by TSQLTableToGrid destructor.
So I end up with TSQLRecord leaking, or double freeing memory block ...
there should be way to tell one of those objects (probably TSQLTableToGrid ?) "hey, it's not your table, don't free it!!"
Or is there such flag somewhere?
Hi,
so I have this problem, I need to print some 'regional' characters. They're handled incorrectly.
System character set is windows-1250.
Regular Canvas.Textout handles these characters correctly.
Here's the screenshot (left - Edit1 on the left, report on the right)
and the code
DrawText('direct ' + Edit1.Text);
DrawText('utf8ToString' + UTF8ToString(Edit1.Text));
DrawText('StringToUTF8' + StringToUTF8(Edit1.Text));
yeah, I know those conversions doesn't make sense but I just wanted to try everything before asking for help
I did so. I'm sorry, I was pretty sure you use svn So I didn't updated the source archives, just commited the lates version to svn
GO for the standard buttons!
can't wait to see that, especially the UIEdit form which seems to be perfect for quick object population / edition.
so you can use the exact same code for a rich Client or a HTML page...
I don't get that, probably because I have absolutely no experience with web services in Delphi. Not even 'hello world'
ok, I'm looking forward to hear your proposals
I don't know "RTTI callbacks" so I'll have to investigate them a bit
But anyway it might be convenient to expose them somehow, in case I want to do validation on my own (in example - creating own editor for a model) so that I can handle validation messages.
To be honest, I prefer those most standard buttons and others components look, over the "glassy" look of recent Office interfaces.
Which version of Delphi do you use?
Delphi 7PE for my "personal" projects By "personal" I mean each project done in my spare time.
Do you plan to use the Delphi Ribbon component available since Delphi 2009?
not for personal projects
I think I could use it, or (even better for compatibility with all Delphi versions) standard VCL components (Win32 Button, Tabs, ToolBar)....
What I love about the framework is it's compatibility with D7... Please, don't break it that way !
Compiler defines would do the thing here, I believe.
that event handler is pretty good idea for a quick fix, but doesn't solve the problem in my opinion.
first of all, I think it'd be good to keep the logics separated from presentation layer, so the validation part should be done on the Record, not the editor.
Let's say I need to store IP address in a record, and I have pretty much ways of adding/editing record (for example, creating the new record directly or modifying it's data inside of a grid). No matter what "editor" I use, the "IP" fields still needs to be valid IP address.
From what I learned using PHP frameworks, automated data validation is very important and convenient. My suggestion is to expand TSQLRecord with methods like this:
//pseudo code
// run the validation of data
// implemented in TSQLRecord,
// calls the GetValidationRules and check data against rules one-by-one.
// Validate method is called each time the object is about to be saved
// or is edited
function Validate(): boolean;
// returns list of rules
// actually implemented by final (inheriting) class.
// each validation rule consists of
// * field name to which rule applies,
// * rule name or even better - rule class
// * rule additional parameters (string - JSON-formated? array of const? I dunno ...)
function GetValidationRules: TValidationRules;
// registers a callback to be called on validation error
procedure RegisterValidationCallback(CB: TValidationCallback);
// Field: field name
// Message: message returned by validation rule
// FieldValue: proposed field value / field value after filtering
// AllowContinue: if set to true, it means there was no error in fact, the validator acts as a "filter" here (see below)
TValidationCallback = procedure (const AField: string; AMessage: string; AFieldValue: string; AAllowContinue: boolean)
example filters:
TRegexpRule:
* checks given value against regexp provided as parameter. if it doesn't pass, do callback, with message "sorry it doesn't validate against regexp"
TToLowercaseFilter:
* calls Callback with AAllowContinue = true and AField = lowercase(OriginalFieldValue)
this doesn't throw an actual error but ensures the field will be lowercase.
actually it might be wise to separate Rules from Filters, or give them separate callbacks.
implementation of GetValidationRules for my case would return array looking like this:
[['Email', TEmailRule, 'allowedDomains:.com;.pl;.eu;.us'],
['Userip', TIPRule],
['Userlogin', TLowercaseFilter],
['Userlogin', TStringLengthRule, 'minLength:5|maxLength:25']]
that is just an idea, I know many things might be hard to implement in delphi, they might not be optimal etc but hopefully I know what I have in my mind.
That way one would be able to simply define filters, expand filtering (creating own filters), handle errors (for example, when creating custom "editor" for the record, just registerValidationCallback and add labels containing errors for each field).
What do you think about it?
Hi,
let's say I have a simple record adding procedure, which creates record and fills it with a data provided by user (possibly using SQLite3UIEdit). What is the proper approach for validating data (like, checking if the string is not too short, or - as in my case - validate the provided string against regexp).
thanks,
migajek
Hi,
is there any progress in making the Framework independent from TMS? I'm about to start a bit more serious project with Synopse Framework, as for now I can use TMS Pack demo I guess, but one day I'll need to make my app independent ...