You are not logged in.
Pages: 1
Hi,
I have to connect to an axisting Microsoft SQL Server with several fields declared as DECIMAL(19, 5).
Currency only supports 4 decimal digits, and Double (the data type mORMot 'sees' these columns) is a floating point data type, so rounding errors could happens.
How do you suggest to handle these columns ?
Thanks!
You guys have the best support a developer can dream!
Many thanks for the quick fix!!
Hi Arnaud,
starting from this (github) commit:
commit e4e52e6605cb7935c1828c93fcff979cb8092ae5 (HEAD)
Author: Arnaud Bouchez <fakeaddress@synopse.info>
Date: Fri Apr 7 20:02:16 2017 +0200
refactored JSONToObject() to use OOP for maintainability
I got a problem:
I define a method like this:
procedure Proc3(Dummy: integer; InParam: TMyClass; out Param1: TMyClass);
the method is successfully called, but an 'Invalid pointer operation' is raised exiting the method implementation.
Note that the problem arises only if the input parameter of type object is defined.
on
procedure Proc2(Dummy: integer; out Param1: TMyClass);
there are no problems.
I tracked down the issue until the TServiceMethodExecute.AfterExecute procedure:
for i := 0 to ArgsUsedCount[smvvObject]-1 do
fObjects[ i ].Free;
It seems that the fObjects[0] reference (the input parameter) is not valid when Free is called.
With the previous commit, the problem is not there.
To ease your debug, I uploaded to
https://drive.google.com/file/d/1S7OcY2 … sp=sharing
a small demo that triggers the problem when Proc3 is called ( eg pointing a browser to http://localhost/root/test/proc3?Dummy=42 )
Thanks a lot fr your support!
Cause I consume some interface based services from an AJAX client, trying to follow a REST approach, so for example I have a service
/customers/order
I can call with GET/PUT/POST/DELETE to perform CRUD operations.
I'd like to consume the same methods from a Delphi client, w/o changes on server side.
If I am not wrong with method based services this is a no-issue, but I LOVE the design that interface based methods enforces on the server!
Sounds strange to me nobody else has similar needs. Am I missing something ?
Hi Ab,
I've re-read TFM
as I remembered it says that: The pure Delphi client implementation will use only POST.
Your first suggestion to use URL encoded params with a GET verb does not meet my needs because does not cover PUT and DELETE verbs.
Just to explain better my question, my problem is NOT about how to encode params, but only about the HTTP verb to use, ie how to make a
DELETE /interface/method or
PUT /interface/method
call from Delphi, with the params in the body as for the 'standard' call that delphi do, a POST
If nothing easy/ready is already implemented, please just only point me in the right direction, any suggestion from the Guru will be welcome! :-)
Thanks for your support!
Hi all,
as far as I understand, invoking an interface method from a delphi client translate in a
POST /interface/method
call. Is there a way to force the client to make the same call with different HTTP verbs ?
Hi ab,
I'm digging a bit more into the problem, and seems a bit more complex than I thought (wrong assumptions by me), but not a mORMot related issue.
Sorry for wasting your time, thank you!
Hi!
some time ago browsing the mORMot source code I spotted a way to switch between URL-encoded vs JSON body way to send parameters to a HTTP DELETE method, or so it seemed to me :-), but unfortunately I no longer found it.
Can you confirm that such option exists, and if so how to use it ?
If it does not exists, is it easy doable ?
I'm asking for such an option because the client of the mORMot server I'm developing only can send parameters of a DELETE method in the body, not URL-encoded.
Thanks!
Guido Aspesani
Thanks for the answer Chaa,
it is exactly what I have tried, but it seemed that the first header specified ( Content-Type ) is 'lost' and does not reach the client
Now I had tried swapping the 2 headers ( Content-Disposition first, then Content-Type ) and it seems work!
a little bit odd, but maybe I'm just ignoring something.
Thanks again!
Hi,
I need to add not only a HEADER_CONTENT_TYPE header, but also a:
Content-Disposition: attachment; filename=<a server side suggested file name>
but seems that only the last header added to the property TServiceCustomAnswer.Header is sent back to the client
The OnAfterURI event can't be used because it does not know the suggested file name. (it is determined inside the interface based service).
Same as for overriding the URI method of the TSQLRestServer.
I'm NOT using the last version of the framework (don't ask! ) maybe is something already solved in latest versions?
If not, any suggestion on how to do it ?
Thanks!
Hi ab,
here is the call stack:
SynCommons.TDynArrayHashed.HashAdd((no value),102850746,-1)
SynCommons.TDynArrayHashed.FindHashedForAdding((no value),True,102850746)
SynCommons.TDynArrayHashed.AddAndMakeUniqueName('ID')
SynOleDB.TOleDBStatement.BindColumns(Pointer($3B97368) as IColumnsInfo,$2D4C880,((0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, nil, nil, nil, 0, 0, 0, 0, 0, 0, 0, 0)))
SynOleDB.TOleDBStatement.FromRowSet(Pointer($3B97360) as IRowset)
SynOleDB.TOleDBStatement.ExecutePrepared
SynDB.TSQLDBConnectionProperties.Execute('SELECT F.ID, F.DESCRIZIONE, F.IDPADRE, F.TITOLOFORM, F.ABILITATO, F.ID_CMTMODULI,PROF.bAbilitaInsert, PROF.bAbilitaEdit, PROF.bAbilitaDelete,MENU.FUNZIONE, MENU.NOME_ICONA FROM CMTFUNZIONI F JOIN SYS_FUNZIONI_HTML5 MENU ON F.ID = MENU.ID_CMTFUNZIONI LEFT OUTER JOIN CMTFUNZIONI PADRE ON F.IDPADRE = PADRE.ID JOIN CMTMODULI ON F.ID_CMTMODULI = CMTMODULI.ID JOIN CMTEXPROFILIFUNZIONI PROF ON PROF.ID_CMTFUNZIONI = F.ID OR Prof.ID_CMTFUNZIONI=0 JOIN CMTEXUTENTIPROFILI C ON Prof.ID_CMTEXPROFILIUTENTE = C.ID_CMTEXPROFILIUTENTE JOIN CMTUTENTI U ON C.ID_CMTUTENTI = U.ID WHERE U.ID=? AND ((F.ID_CMTMODULI=? AND F.VISIBILE=''T'') OR ( F.VISIBILE=''F'' AND F.ABILITATO=''F'' AND F.EXE_PARAMS=''HTML5'' )) ORDER BY PADRE.INDICE, F.INDICE',(...),nil,False)
BFiveService.UtenteService.TUtenteService.Profilo($30C5278,$2E214F0,$30D4F10)
The framework is updated at Now()
The statement in my code that originates the problem is something like
Qry := ConnectionProperties.Execute(SQLText, [User.ID_CMTUTENTI, ModuleID]);
The statement is used in a loop, executed a few times with differents parameter's values
Hi,
just updated to the last and greatest mORMot from git, from a while ago.
A strange behaviour is hitting me. I tracked down the issue to the method
TDynArrayHashed.HashAdd(const Elem; aHashCode: Cardinal; var result: integer);
var n,cap: integer;
begin
n := Count;
SetCount(n+1); // reserve space for a void element in array
cap := Capacity;
if cap*2-cap shr 3>=fHashsCount then begin
// fHashs[] is too small -> recreate
ReHash;
result := HashFind(aHashCode,Elem); // fHashs[] has changed -> recompute
assert(result<0);
end;
with fHashs[-result-1] do begin // HashFind returned negative index in fHashs[]
Hash := aHashCode;
Index := n;
end;
result := n;
end;
I do not use this class direclty, it is used by SynDB since I'm connecting to a SQL Server 2008 to access axtrernal data.
in my findings when the statement
n := Count;
sets n to zero, fHashs is not properly allocated, thus resulting in an AV when the statement
with fHashs[-result-1] do begin // HashFind returned negative index in fHashs[]
Hash := aHashCode;
executes.
I temporarily solved commenting out the check
//if cap*2-cap shr 3>=fHashsCount then begin
and thus always executing ReHash;
Of course in this way all your speed optimizations are gone with the problem
Verified with XE4/XE7 targeting win32, under Win7 64 bit.
I'm sorry but I was not able to better track the 'root' issue, I suspect this is only a consequance effect of something more deep.
If you need more details I'm here to help.
ab,
interesting hint:
Tier 1 = client, Tier 2 = application Server, Tier 3 = business logic server, Tier 4 = DB
how is implemented Tier 3 in your diagram?
Simply a set of classes or you are thinking in something more mORMot-ish ?
Thank you very much Chaa, very usefull hints!
Hi Arnaud,
I'm working on a project where mORMot serves interface based requests to a JavaScript client, auth enabled.
Last autumn I noticed that updating to the latest mORMot sources, JS side of authenticatio stopped working.
I finally found the time to track down the exact commit that introduced the anomaly, and it is the
5489231f2a34fda71b22f6f8a00a8cd1d6d209d0 dated November, 2 2015
The only change introduced in this commit is from:
TSQLRestServerAuthenticationSSPI = class(TSQLRestServerAuthenticationURI)
to:
TSQLRestServerAuthenticationSSPI = class(TSQLRestServerAuthenticationSignedURI)
I'm not using integrated security, so I'm wondering how this change can stop the server authorizing my requests.
Note that the first 2 requests that authenticate the user ( auth?UserName= and auth?UserName= &Password= ) are succesfull.
Probably I can solve my problem easly and disable SSPI authentication undefining SSPIAUTH, but I'd like to learn more of this wonderful piece of code that is mORMot and master each aspect of it!
Update:
maybe it is not a mORMot issue at all, I'm checking with the javascript developer the issue: if I make the request with Auth enabled BUT using the address bar of the browser the response is ok and I see the original file.
Sorry
Thanks for the StringFromFile() hint!
Sadly this not solve the issue.
You are right , the compression/decompressio is on the wire and should be done transparently by the browser/server. What I see are the debug infos of the developer tools.
In the correct situation I see comprssed data (I gess it, is like garbage) while in the not-working situation I see the same data as in the original file on disk, in the case of a PDF file something starting with the sequence
%PDF-1.4
The only remarkable difference is auth turned on or off on the mORMot server. Auto = on, not working, Auth = off working.
I'm pretty sure that there are something else that is causing the different behaviours, somehow a conseguence of Auth, but I don't found it
Hi,
I wrote an interface based method that returns a TServiceCustomAnswer. Basically, accept an ID and searches for a corresponding file to send back to the client.
with authentication disabled, if I make a request using the browser and typing directly on the url, it works, an returns, let's say, a PDF.
The browser can view it with no prob and save it on disk where I can compare with the original file. No differences.
But if I enable authentication on the mORMot server, and I use a javascript client to access the same url, the size of the dowloaded file is about the double.
I suspect is an encoding problem, but I don't see any difference on the 2 request to explain it.
In the failing situation, the response from mORMot starts with :
%PDF-1.5
so it seems uncompressed. The request contains
Accept-Encoding gzip, deflate
and the mORMot response :
Accept-Encoding synlz,gzip
Access-Control-Allow-Meth... POST, PUT, GET, DELETE, LOCK, OPTIONS
Access-Control-Allow-Orig... *
Access-Control-Expose-Hea...content-length,location,server-internalstate
Access-Control-Max-Age 1728000
Content-Length 1762067
Content-Type application/pdf
while the working answer (no auth serverside) shows a compressed stream
the rilevant mORMot's service code is:
if UpperCase( extension ) = 'PDF' then
Result.Header := HEADER_CONTENT_TYPE + PDF_CONTENT_TYPE
else
Result.Header := HEADER_CONTENT_TYPE + BINARY_CONTENT_TYPE;
RawByteStream := TRawByteStringStream.Create;
MemStream := TMemoryStream.Create;
if FileExists(PhisicalFileName) then
MemStream.LoadFromFile(PhisicalFileName)
else
raise Exception.Create('File ' + PhisicalFileName + ' not found. ID=' + IntToStr(FileID));
MemStream.SaveToStream(RawByteStream);
Result.Content := RawByteStream.DataString;
Another fact that makes me suspect encoding is the problem, is that if the file is a text file, everything is ok in both situations! (with and W/o auth)
Am I miss something?
As I hoped, it was easy, once figured out what was going on
Just added :
if not Props.MainConnection.Connected then
Props.MainConnection.Connect;
right before the StartTransaction:
Props.MainConnection.StartTransaction;
The connection pool was creating a new connection without opening it, and the check for support for transactions is done after the connection is opened, so pening the connection before using it with a SQL statement do the trick.
Thanks for your time AB!
AB, I'm trying to apply your (always valuable) suggestion.
It seems to me that TSQLRestBatch is a perfect choice in a ORM based implementation.
Probably I have not put under your attention that I'm using 'raw' SQL statements, because I have to deal with a given DB and structure, so no ORM here.
This news shreds some light and maybe a new hint?
It seems to me that everything related to transactions handling is already in place, needs only something to start working
In my interface-based server I define an external DB connection to a Microsoft SQL server 2008 with:
ExternalDBProperties := TOleDBMSSQLConnectionProperties.Create(ServerName, DataBaseName, UserID, UserPassword);
then in my services I can successfully execute SQL statemts against the external DB:
Props := TSQLDBConnectionProperties( TSQLRestServerDB( SC.Factory.RestServer).Model.Props[TSQLAuthUser].ExternalDB.ConnectionProperties);
Props.ExecuteNoResult(SQLStatement, []);
But how can I handle transactions? I use
Props.MainConnection.StartTransaction / Commit / RollBack
but Props.MainConnection.TransactionCount is always zero after a StartTransaction so subsequents Commits/Rollbacks fails.
How transactions on external DB should be handled ?
I converted the code as suggested by you, but I can't figure out how handle the case of a property of kind array, without passing to another string/json initialization:
For example in the case of a JSON object like this:
{
"OrderNo": "123",
"Items2: [
{"Description": "Item 1", "Quantity": 10},
{"Description": "Item 2", "Quantity": 20}
]
}
I check if the property Items is present with
if MyVariable.GetValueIndex('Items') > -1 then ...
and then how I can access each single Item?
So far the only solution I found is to assign the value of the property Items to a string variable, obtaining the JSON representation of the array, and then initialize another TDocVariantData variable with that string.
Is it the only way or there is something smarter?
An edit for the readers: the correct initialization code for MyVariable is
MyVariable.InitJSON(MyParam);
Do you mean something like this ?
var
MyVariable: TDocVariantData;
begin
MyVariable := TDocVariantData.InitJSON(MyParam);
// now I can do thing like
if MyVariable.GetValueIndex('PropertyName') <> -1 then;
end;
Tanks ab!
It took me a while to experiment a little. Here is how I done it:
The method is defined as
MyInterface.MyMethod(MyParam: RawJSON);
in the implemetation I have
var
MyVariable: variant;
begin
MyVariable := _Json(MyParam);
// now I can do thing like
if MyVariable.Exists('PropertyName') then;
end;
and thus run only needed SQL statements. Great!
First of all I want to thanks the man who made my job easier and enjoyable: Thanks Arnaud!
I'm writing an interface based service to provide a REST access to a legacy DB (so no ORM here), using DTO as parameters to move data around, and everything is fine.
Since some DTO is quite complex (with nested objects and array of objects), I'd like to execute updates and/or inserts on the underling DB only if strictly needed, that is only if the client has really modified a certain sub-property.
So for example if in a DTO there is an array property, and I call a service using PUT passing a JSON representation of that DTO with this property 'missing' (not an empty array, [], but nothing at all), this should be interpreted as 'leave that property/array as is, don't touch it', avoiding maybe a lot of SQL statements.
The JSON object is deserialized into my DTO, but I lose the knowledge of the existence or not of the property in the original JSON object
Since mORMot is a really smart pet, I suspect there is some way to achieve that goal.... or am I missing something?
Many thanks!
Guido
@jairgza:
any advance with your idea?
I was wondering exactly the same: Build a very light server that handles only few things like configuration and administration, and when requested with a interface-type service load the module that will handle it.
Don't know if unloading the module after the use will be a plus or a performance minus... in a development environment it is certainly a plus, but in production....
Thanks Arnaud, your answers are always quick and useful!
As stated by SAD document Rev. 1.18, page 283:
TSQLRestServerCallBack method-based services - i.e. published methods of the inherited
TSQLRestServer class as stated below (page 312) - must be implemented to be thread-safe by
default;
After doing some research (sadly I never worried about writing thread-safe code or not) I still have some doubt.
1) In general, to claim a pice of code is thread-safe, is it suffice avoid referencing 'global' variables?
Are 'local' variables 'safe' because are stack-allocated an thus each thread executing the code allocates his own 'copy' of local variables?
2) SAD documentation state also:
At RESTful / CRUD level, Add/Update/Delete/TransactionBegin/Commit/Rollback methods are
locked by default (with a 2 seconds timeout), and Retrieve* methods are not;
For example this implementation of a method-service:
does this mean that, if I nedd to access DB data in a method-service, I don't have to care about thread-safety because the framework does it for me?
Considering this pseudo-implemetation of a method-service:
procedure TMyTSQLRestServerDBDescendant.MyMethod(Ctxt: TSQLRestServerURIContext);
var
integervar: integer;
stringvar: string;
counter: integer;
begin
try
integervar := DoSomethingAndGetAValueForMyIntegerVar;
stringvar := DoSomethingAndGetAValueForMyStringVar(integervar);
// here I get a counter value from DB (an Ivoice counter, for example) and increment it
// for later use, so it is VERY important that two concurrent threads have two different
// values for the counter
counter := GetCounterValueFromDBAndIncrementIt;
DoStuffWithMyCounter(counter);
Ctxt.Success;
except
on E: Exception do
Ctxt.Error('Error: ' + E.Message, HTML_BADREQUEST);
end;
end;
where obviously called procedures like DoSomethingAndGetAValueForMyIntegerVar don't reference any global variables, and GetCounterValueFromDBAndIncrementIt does not use any critical section or lock assuming the framework itself does (see 2) above).
does it seems thread-safe to your expert eyes?
Just to 'close' the thread...
I successfully built a method service using POST requests as hinted by Arnaud, and with the very valuable help of DigDiver that shared his TMimeDecoder class that I have slightly modified ( with Delphi XE4, ExtractHeaderFields returns 'values' in the Strings parameter with still the double quotes round them, so the filename field is not usable 'as-is', not sure how it works for you DigDiver)
Again thanks to you guys!
Now I have to be sure that my implementation is thread-safe, but this is worth another..... well... thread
@CycleSoft
Good point.
We may allow other parameters in addition to TServiceCustomAnswer record (perhaps renamed TServiceCustomRequest ) but expecting them to be encoded at the URI level.
In all cases, this would work only for TSQLRestRoutingREST kind of routing, in its ExecuteSOAByInterface method.
My knowledge of mORMot (and HTTP-world in general) is too superficial to be meaningful, but my impression is that if to implement this feature you have a hard time and limitations, maybe this is not the right direction and there is a cleaner solution?
A newbie (like me) will easily be tricked, I don't even know what 'TSQLRestRoutingREST kind of routing' is
If you already are using the 'method' approach for the BLOBs, there will be a good reason, so I prefer learn something new and adopt your approach.
That said, can you only point me in the right direction? Can I find a start point in the documentation or is better to search your source code?
Note that DigDiver code uses a method-based service, not an interface-based service here.
OK!
..and about the links to the softwares created with mORMot, I will add a link to mine with pleasure!
In fact, another question I have is how I can mention mORMot in my application(s)? Something like 'powered by mORMot' and a link?
Thanks for such a quick reply!
@ab: if I undestend well your proposal to add support for incoming raw content for interface-based services, then there will be one and only one TServiceCustomAnswer parameter, right?
If so, how the interface service will be able to properly handle such data, without any other input parameters such, say, an ID Customer to link the content to?
@DigDiver: thanks for sharing your code!
I'm not yet able to undestend well this 'low-level' use of mORMt... guess I have to dig a little under the surface!
Anyway thanks for pointing me in this direction!
Hi,
First af all, I have to say thanks for this great artifact called mORMot!
I'm following it for a while, waiting the right project to use it, and finally the day is come!
I started to develop the backend, and I have some questions/suggestion requests on various topics, so I will ask them one by one in the hoping to help someone like me approaching for the first time to mORMot.
I followed your advice in this article
http://blog.synopse.info/post/2012/03/2 … ed-service
to return a given file (a PDF in my case) setting
Result.Header := HEADER_CONTENT_TYPE + 'application/pdf';
and it works wonderfully!
Now I have to implement a 'symmetric' method, to upload a binary file to the mORMot server.
Wich is the preferred/optimized way to do it? I mean, there is a way to avoid the Base64 encoding and save bandwidth also uploading?
Pages: 1