You are not logged in.
Hi Arnaud,
Are there any videos from the sessions you did at last years EKON conference?
I'm particularly interested in the one titled 'Embracing mORMot 2.1'.
Thanks,
Esmond
could this be useful to Mormot?
Does anyone have an opinion on when not to use SQLite instead something like of Postgres? I guess it's when there's a large amount of users or data. My question was triggered by this article:
Re: 'Any good link\articles\Books' the 1970's book by Niklaus Wirth 'Algorithms+Data Structures=Programs' is still worth a read.
btw the blog page seems to be down.
I’m not sure that what Utf-
8 is called matters that
much. Happy New Year!
https://github.com/pleriche/FastMM5
May be of interest to mORMot users.
I like fossil. It’s simple.
@ab are you involved in the release of archives from the Vatican today?
Would it make sense to have a ‘todo’ list? It could encourage contributions.
If you use numbers to name the files create a set of folders numbered 1-9 and file the files depending on the first digit of the file name. Or something like that.
Poor mormot - who won a wildlife prize just now
Thanks for the answers.
JSON does seem more KISS
I had this problem ages ago with a JavaScript client and just looked for both (ID || RowID). But that doesn’t probably help here.
I've had another look at this and found a better way to remove the '_'.
@ab please could you have a look at this pull request
Thanks
I'm using a custom TSQLAuthUser record which adds an email address field and then uses TSQLRestServer.OnAuthenticationUserRetrieve to allow a user to use their username or email to login with.
This works except TSQLRestServerAuthenticationDefault.CheckPassword always uses the username to calculate the salt. I think it needs changing from:
function TSQLRestServerAuthenticationDefault.CheckPassword(Ctxt: TSQLRestServerURIContext;
User: TSQLAuthUser; const aClientNonce, aPassWord: RawUTF8): boolean;
var aSalt: RawUTF8;
begin
aSalt := aClientNonce+User.LogonName+User.PasswordHashHexa;
result := IsHex(aPassword,SizeOf(THash256)) and
(IdemPropNameU(aPassWord,SHA256(fServer.Model.Root+CurrentServerNonce(false)+aSalt)) or
// if current nonce failed, tries with previous 5 minutes' nonce
IdemPropNameU(aPassWord,SHA256(fServer.Model.Root+CurrentServerNonce(true)+aSalt)));
end;
to:
function TSQLRestServerAuthenticationDefault.CheckPassword(Ctxt: TSQLRestServerURIContext;
User: TSQLAuthUser; const aClientNonce, aUserName, aPassWord: RawUTF8): boolean;
var aSalt: RawUTF8;
begin
aSalt := aClientNonce+aUserName+User.PasswordHashHexa;
result := IsHex(aPassword,SizeOf(THash256)) and
(IdemPropNameU(aPassWord,SHA256(fServer.Model.Root+CurrentServerNonce(false)+aSalt)) or
// if current nonce failed, tries with previous 5 minutes' nonce
IdemPropNameU(aPassWord,SHA256(fServer.Model.Root+CurrentServerNonce(true)+aSalt)));
end;
i.e. passing the acutual username used
Incidentally TSQLRecordFTS5Unicode61 saves a few % in DB size compared to TSQLRecordFTS4 with the simple tokenizer.
in TSQLRecord.GetSQLCreate in mORMot.pas changing
tokenizer := 'simple';
to
if Props.Kind = rFTS5 then
tokenizer := 'unicode61'
else
tokenizer := 'simple';
should fix it.
Also in the comments it mentions 'unicode64' a few times - I assume that's a typo.
TSQLRecordFTS5 is causing an error in createMissingTables. The simple tokenizer seems to have been removed from FTS
Free Text Search is now included by default
Finally got round to looking at this again. These changes to mORMot.pas seem to fix it:
line 33916:
if Props.SimpleFields[i].Name[1] = '_' then
result := result+copy(Props.SimpleFields[i].Name,2,MaxInt)+','
else
result := result+Props.SimpleFields[i].Name+','; // valid simple fields
at line 33947 the following check needs removing
if (Props.RTreeCoordBoundaryFields<2) or
(Props.RTreeCoordBoundaryFields>RTREE_MAX_DIMENSION*2) or
(Props.RTreeCoordBoundaryFields and 2<>0) then
raise EModelException.CreateUTF8('% has % fields: RTREE expects 2,4,6..% boundary columns',
[Props.Table,Props.RTreeCoordBoundaryFields,RTREE_MAX_DIMENSION*2]);
line 51273/4
if F.Name[1] = '_' then begin
fSQLTableSimpleFieldsNoRowID := fSQLTableSimpleFieldsNoRowID+copy(F.Name,2,MaxInt)+',';
fSQLTableRetrieveAllFields := fSQLTableRetrieveAllFields+','+copy(F.Name,2,MaxInt);
end else begin
fSQLTableSimpleFieldsNoRowID := fSQLTableSimpleFieldsNoRowID+F.Name+',';
fSQLTableRetrieveAllFields := fSQLTableRetrieveAllFields+','+F.Name;
end;
line 51485
if Fields.List[W.Fields[i]].Name[1] = '_' then
W.ColNames[n] := copy(Fields.List[W.Fields[i]].Name, 2, MaxInt)
else
W.ColNames[n] := Fields.List[W.Fields[i]].Name;
the comma in 32526 isn't needed
result := FormatUTF8('%+% %',[result,copy(Name,2,maxInt),Props.Props.SQLFieldTypeToSQL(i)]) else
Please could you review this?
I think the '_' gets changed to a '+' by mORMot which sqlite swallows returning just the field name. I'll look more.
Thanks for https://synopse.info/fossil/info/c96db70ed9 but it still adds an extra comma at the end ('%+% %,') which sqlite seems to ignore.
Thanks for your work!
Another problem... RTREE_MAX_DIMENSION in mORMot.pas becomes invalid if auxiliary columns are used
I've found another problem auxiliary columns. Because the underscore gets removed CreateMissingTables() always thinks the model has changed. This causes an sqlite error saying virtual tables can't be edited. Any suggestions on a solution?
Thanks
I get errors creating rtree tables with auxillary columns. Changing line 32526 of mormot.pas from:
if Name[1]='_' then // auxiliary columns for SQlite3 >= 3.24.0
result := FormatUTF8('%+% %,',[result,@Name[2],Props.Props.SQLFieldTypeToSQL(i)]) else
to:
if Name[1]='_' then // auxiliary columns for SQlite3 >= 3.24.0
result := FormatUTF8('%+% %',[result,copy(Name,2,MaxInt),Props.Props.SQLFieldTypeToSQL(i)]) else
seems to fix it.
Yea!
if you add logging to your project:
TSynLog.Family.Level := LOG_VERBOSE;
The logs will show the SQL created by CreateMissingTables.
I've tried putting mORMotUI.pas both at the start and end of the uses clause and it still gives the same error (using delphi 2007).
If I'm using mORMotUI with a form I can't set any VCL controls' align properties to alRight at run-time. Delphi gives the error:
[DCC Error] Unit1.pas(281): E2010 Incompatible types: 'TAlign' and 'TSQLTableToGridAlign'
As a workaround I've used TAlign(4) instead of alRight. Is there a better way?
Isn't DasBoot better? I expect mORMot can do it but it may take more reading of the docs...
+1 for splitting SynCommons
Maybe defining a record then an array would help
Thank you for adding TLS support. I've tried google's recaptcha and gmail and it seems to work fine. Only thing I've noticed is that when sending emails only port 465 works and not 587.
In case it's useful to anyone below is some code that processes a simple html feedback form with a recaptcha:
Type
Trecaptcha = packed record
success: boolean;
challenge_ts: RawUTF8;
hostname: RawUTF8;
end;
procedure TMyServer.sendFeedback(cTxt: TSQLRestServerURIContext);
var
feedbackEmail, feedbackMessage, gRecaptchaResponse: RawUTF8;
recaptchaSocket: THttpClientSocket;
P: PUTF8Char;
response: Trecaptcha;
Server: TSMTPConnection;
begin
P := PUTF8Char(cTxt.Call.InBody);
if UrlDecodeNeedParameters(P, 'EMAIL,MESSAGE,G-RECAPTCHA-RESPONSE') then begin
while P<>nil do begin
UrlDecodeValue(P,'EMAIL=', feedbackEmail);
UrlDecodeValue(P,'MESSAGE=', feedbackMessage);
UrlDecodeValue(P,'G-RECAPTCHA-RESPONSE=', gRecaptchaResponse, @P);
end;
if (feedbackEmail = '') or (feedbackMessage = '') or (gRecaptchaResponse = '') then
Ctxt.Error('Missing Parameter')
else begin
recaptchaSocket:= THttpClientSocket.Open(
'www.google.com','443',cslTCP, 10000, true);
try
recaptchaSocket.Post('recaptcha/api/siteverify',
'secret=XXXXXX&response='+
gRecaptchaResponse + '&remoteip=' + Ctxt.RemoteIP, 'application/x-www-form-urlencoded');
RecordLoadJSON(response, pointer(recaptchaSocket.Content), TypeInfo(Trecaptcha));
if response.success then begin
if SendEmail('smtp.gmail.com', 'me@me.net',
'me@me.net',
'Feedback Form From: ' + feedbackEmail, feedbackMessage, '',
'me@me.net','xxx', '465', '', true)
then
Ctxt.Success
else
Ctxt.Error('Sorry, message sending failure');
end else
Ctxt.Error('recaptcha error');
finally
recaptchaSocket.Free;
end;
end;
end else
Ctxt.Error('Missing Parameter');
end;
const
__recaptcha = 'success: boolean; challenge_ts: RawUTF8; hostname: RawUTF8;';
Initialization
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(Trecaptcha),__recaptcha);
Okay, Thanks.
I'm using TPrecisionTimer to time a long running procedure. Would it be possible to change the Time function to return hours and minutes as well?
Changing MicroSecToString in SynCommons.pas seems to work:
function MicroSecToString(Micro: QWord): RawUTF8;
function TwoDigitToString(value: cardinal): RawUTF8;
var L: integer;
begin
UInt32ToUtf8(value,result);
L := length(result);
if L=1 then
result := '0.0'+result else // '3' -> '0.03'
if L=2 then
result := '0.'+result else // '35' -> '0.35'
insert('.',result,L-1); // '103' -> '1.03'
end;
var seconds: Cardinal;
begin
if Micro<=0 then
result := '0us' else
if Micro<1000 then
result := SmallUInt32UTF8[Micro]+'us' else
if Micro<1000*1000 then
result := TwoDigitToString(Int64Rec(Micro).Lo div 10)+'ms' else
if Micro<1000*1000*60 then
result := TwoDigitToString(Micro div (10*1000))+'s' else
begin
seconds := Micro div (1000*1000);
if seconds < 60*60 then
result := UInt32ToUtf8(seconds div 60)+'m '+UInt32ToUtf8(seconds mod 60)+'s'
else
result := UInt32ToUtf8(seconds div (60*60))+'h '+
UInt32ToUtf8((seconds mod (60*60)) div 60)+'m '+
UInt32ToUtf8(seconds mod 60)+'s'
end;
end;
It could be a missing '/'
rather ironically the link above hangs on my iphone5 chrome browser when I scroll down to 'Too much documentation can kill the documentation!'.
It's really convenient to have online documentation but would there be any way to divide up the html pages to make it more responsive?
Sorry, I misread undefined in the comments as uninitialised.
I want to create a method based service which works for both logged in and anonymous users. Is it safe to do something like this?:
procedure TMySQLRestServer.search(cTxt: TSQLRestServerURIContext);
begin
if cTxt.SessionGroup = 0 then
//return results for anonymous users
else
//return results for logged in users
end;
I only ask as the code comments
/// the corresponding TAuthSession.User.GroupRights.ID value
// - is undefined if Session is 0 or 1 (no authentication running)
SessionGroup: integer;
but it seems that I can rely on SessionGroup being 0 rather than undefined if no authentication is running.
Have you called CreateMissingTables()?
If you use JSON1 remember to call TSQLDataBase.CacheFlush() after edits to keep the ORM cache in sync. I got caught out by that!
My tuppence worth is that haven't found a javascript framework that's helpful. I prefer to code as much as possible by hand. Admittedly I do use jQuery and Bootstrap (by twitter) for a modern UI (don't much like dealing with css). There is also fuelux if you want some fancy controls like a datepicker.
Thanks, I should have spotted that.
I got confused by using ObjectToJSON() in syncommons as an example. It seems to use the class constructor in an unusual way.
I'm trying to use TTextWriter with a simple test:
procedure test;
var
tw: TTextWriter;
begin
with tw.CreateOwnedStream do
try
finally
Free;
end;
end;
This gives an Access Violation. but if I declare the TTextWriter globally like:
var
tw : TTextWriterClass = TTextWriter;
it works. Do TTextWriter vars need to be declared globally or am I missing something?
Have a look at:
ServiceMethodByPassAuthentication
The parameters seem to suggest that
Great, thanks for the quick response!
I'm having a problem with invalid timestamps when making two requests in quick succession after logging in. The first request succeeds but the second one fails.
In the logs I've got: Invalid TimeStamp: expected >=-8, got 142
The problem seems to be in line 51786 of mormot.pas when result.fLastTimeStamp is less than fTimeStampCoherencyTicks, I assume causing an overflow:
if HexDisplayToCardinal(PTimeStamp,aTimeStamp) and
(fNoTimeStampCoherencyCheck or (result.fLastTimeStamp=0) or
(aTimeStamp>=result.fLastTimeStamp-fTimeStampCoherencyTicks)) then begin
could using abs() could be a solution?:
(aTimeStamp>=abs(result.fLastTimeStamp-fTimeStampCoherencyTicks))) then begin
Also in line 51803 it's failing to log the timestamp value and looks like it should be:
Ctxt.Log.Log(sllUserAuth,'Invalid TimeStamp: expected >=%, got %',
[result.fLastTimeStamp-fTimeStampCoherencyTicks,Int64(aTimeStamp)],self); //<<< added Int64(aTimeStamp)