You are not logged in.
In my application, I encountered an issue with mORMot 2: dynamically loading libcurl via LibCurlInitialize([giAll], 'libcurl-4.dll') results in a call to SetDllDirectoryW(nil). This breaks subsequent LoadLibrary calls for loading third-party DLLs. The problem arises because my application calls SetDllDirectoryW at startup to set a global DLL directory.
After analyzing the DLL loading algorithm in mORMot 2, I would like to propose a few improvements:
1. When loading libcurl, use the TryFromExecutableFolder option instead of forcibly appending the absolute path Executable.ProgramFilePath + dllname (which, by the way, is done without checking whether dllname is relative or absolute).
// mormot.lib.curl
procedure LibCurlInitialize
...
curl := TLibCurl.Create;
{$ifdef OSWINDOWS}
curl.TryFromExecutableFolder := True; // << fix
{$endif OSWINDOWS}
curl.TryLoadResolve([
//{$ifdef OSWINDOWS}
// first try the libcurl.dll in the local executable folder
//Executable.ProgramFilePath + dllname, // << fix
//{$endif OSWINDOWS}
// search standard library in path
dllname
...2. Inside TryLoadLibrary, invoke LibrarySetDirectory only if the path differs from Executable.ProgramFilePath.
// mormot.core.os
function TSynLibrary.TryLoadLibrary
...
if (nwd <> '') and (nwd <> Executable.ProgramFilePath) then // << fix
begin
GlobalLock; // SetDllDirectoryW() is for the whole process not thread
if not LibrarySetDirectory(nwd) then // as documented on microsoft.com
begin
GlobalUnLock;
nwd := '';
end;
end else
nwd := ''; // << fix3. Add a global variable LibraryUseSetDllDirectory to mormot.core.os to allow completely disabling SetDllDirectoryW within the framework.
// mormot.core.os
var
LibraryUseSetDllDirectory: boolean; // = False by defaultI wrote this code myself for fast batch deletion (it runs inside a transaction, of course):
I := 0;
VLen := Length(AMarkIDs);
while I < VLen do begin
VCount := Min(MAX_SQLPARAMS, VLen - I);
VIds := Int64DynArrayToCSV(@AMarkIDs[I], VCount, '(', ');');
Inc(I, VCount);
Result := FClient.Execute('DELETE FROM ... WHERE RowID IN ' + VIds);
...
end;I'm curious why you changed the parser behavior in mORMot 2 and set the parameter limit to only 64, considering that SQLite3 supports much higher limits.
Did you accidentally mix up the MAX_SQLFIELDS and MAX_SQLPARAMS constants?
In my case, I’ll pass the RowIDs as literals instead of parameters, as it doesn’t make any practical difference here (as I suppose).
Sorry for reviving such an old thread, but I ran into this issue while migrating to mORMot v2.
In my case, I'm trying to delete 89 records using a DELETE FROM ... WHERE RowID IN (...) statement and I get a "Too many parameters..." error.
In the old codebase, I had an internal limit of 900 parameters for such operations (SQLite allows anywhere from 999 to 32,766, depending on the version).
In mORMot there is a MAX_SQLPARAMS = 500 constant, but it doesn't seem to be taken into account during parameter parsing. Instead, MAX_SQLFIELDS (which defaults to 64) is used: https://github.com/synopse/mORMot2/blob … e.pas#L842
Is this a bug, or is it the intended behavior?
Try this example: https://claude.ai/share/dbed8271-6891-4 … c1e580fc47
Thank's a lot, I feel a bit stupid but I have to ask - How should I connect this function to the list?
Just read the comments carefully:
If the same Query-SQL is used in an external app (sqlitebrowser), the results are fast on any table (23ms).
A very interesting effect. What could explain this?
Davide
You can find it here: https://github.com/synopse/mORMot2/blob … t.pas#L666
@FlaviusFX I think you should use the source from the lts-2.3 branch. It contains backported fixes for bugs that were discovered after the 2.3 stable release.
So, it looks like we need the new 2.3.1.stable, or something similar. Or is version 2.4 coming soon?
There's also an issue with compiling mormot 1: [dcc32 Fatal Error] SynMongoDB.pas(6863): F2084 Internal Error: C2802. https://github.com/synopse/mORMot/blob/ … .pas#L6863
Also, due to the new noreturn directive, a warning has appeared: [dcc32 Hint] SynCrypto.pas(6282): H2077 Value assigned to 'ThreadWrapper' never used.
function ThreadWrapper(var P: TThreadParams): Integer; stdcall;
begin
with P do
AES.DoBlocks(bIn,bOut,bIn,bOut,BlockCount,Encrypt);
ExitThread(0);
result := 0; // make the compiler happy, but won't never be called
end;You had to add an unnecessary assignment to result in the code to make one compiler version happy, but now a new one has appeared and it's sad...
The variables of this type are used in two functions as parameters: SqlDriverConnectA and SqlDriverConnectW.
On Win64, since parameters are passed through registers and the HWND type fits within 32 bits, no fatal errors occur in practice.
However, the type declaration should still be corrected for proper compatibility and clarity.
Hi,
I noticed that the type SqlHWnd is currently declared as
SqlHWnd = LongWord;But, according to the ODBC API, SQLHWND is defined as HWND, which itself is declared as void*.
Declaring it as LongWord forces it to 32 bits, which breaks compatibility on 64-bit Windows.
It should instead be declared as a pointer type (PtrUInt).
TSqlDBConnectionProperties.ForcedSchemaName https://github.com/synopse/mORMot2/blob … .pas#L1854
As some might have noticed, we have placed the first release candidate of the Free Pascal Compiler version 3.2.4 on our download servers already for some time.
That "some time" was actually 11 months ago.
If you try to work with a mORMot-created database in external applications, you'll likely encounter error like: "no such collation sequence: SYSTEMNOCASE"
This is because mORMot uses custom collations (like SYSTEMNOCASE) by default, which are not available in standard SQLite3 builds.
To fix this, I’ve released a lightweight SQLite3 extension that adds these missing collations, allowing you to open and modify mORMot databases in external tools (like the SQLite CLI or SQLiteStudio) without errors.
Supported Collations: WIN32CASE, WIN32NOCASE, SYSTEMNOCASE, UNICODENOCASE (new in mORMot 2), ISO8601.
Instruction, precompiled binaries and source code are available here: https://github.com/zedxxx/sqlite3-mormot-collate
The mormot.core.os.windows.inc file already includes the fastest method, i.e. GetFileAttributesEx
But, the article says that the FindFirstFileEx is the fastest one.
Read the link from my previous post, it answers to all your questions.
MPL/GPL/LGPL Three-License
The framework is licensed under a disjunctive three-license giving you the choice of one of the three following sets of free software/open source licensing terms:
Mozilla Public License, version 1.1 or later;
GNU General Public License, version 2.0 or later;
GNU Lesser General Public License, version 2.1 or later.
This allows the use of our code in as wide a variety of software projects as possible, while still maintaining copy-left on code we wrote. See the full licensing terms.
In SQLiteStudio you can define your own collation (Tools - Open collations editor) and open DB as usual.

For the SYSTEMNOCASE you can try this snippet:
function system_nocase(a, b)
{
a = a.toUpperCase();
b = b.toUpperCase();
return (a < b ? -1 : (a > b ? 1 : 0));
}What is the minimum supported Delphi version for this? Is it 2009?
libcurl backend supports http2.
ZIP supports AES encryption since version 5.2: https://en.wikipedia.org/wiki/ZIP_(file … Encryption
The word "Asynchronous" is usually abbreviated as async, not asynch: https://i.imgur.com/i4MiMIn.png
It works for me with SynSQLite3Static and sqlite3.dll (both tested only for Win32). I used precompiled dll https://www.sqlite.org/download.html
mORMot 1.18.6192, Delphi 10.3.3 CE, test code:
program SynFTS5Test;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
SynCommons,
SynSQLite3,
SynSQLite3Static,
mORMot,
mORMotSQLite3;
type
TSQLRecordFTS5Trigram = class(TSQLRecordFTS5);
TSQLTextRecord = class(TSQLRecordFTS5Trigram)
protected
FText: RawUTF8;
published
property Text: RawUTF8 read FText write FText;
end;
var
VModel: TSQLModel;
VClient: TSQLRestClientDB;
begin
//sqlite3 := TSQLite3LibraryDynamic.Create;
Writeln(SYNOPSE_FRAMEWORK_FULLVERSION);
try
VModel := TSQLModel.Create([TSQLTextRecord]);
VClient := TSQLRestClientDB.Create(VModel, nil, 'fts5.db3', TSQLRestServerDB);
try
VClient.Server.CreateMissingTables;
// ...
finally
VClient.Free;
VModel.Free;
end;
except
on E: Exception do begin
Writeln(E.ClassName, ': ', E.Message);
end;
end;
Writeln('Press ENTER to exit...');
Readln;
end.Do you link with sqlite3.dll or with SynSQLite3Static.pas?
mORMot version: 1.18.6171
Test code:
uses
System.SysUtils,
SynCommons,
SynSQLite3Static,
mORMot,
mORMotSQLite3;
type
TSQLTextRecord = class(TSQLRecordFTS5)
protected
FText: RawUTF8;
published
property Text: RawUTF8 read FText write FText;
end;
var
VModel: TSQLModel;
VClient: TSQLRestClientDB;
begin
Writeln(SYNOPSE_FRAMEWORK_FULLVERSION);
try
VModel := TSQLModel.Create([TSQLTextRecord]);
VClient := TSQLRestClientDB.Create(VModel, nil, 'fts5.db3', TSQLRestServerDB);
try
VClient.Server.CreateMissingTables;
// ...
finally
VClient.Free;
VModel.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.rises exception:
ESQLite3Exception: Error SQLITE_ERROR (1) [Step] using 3.33.0 - no such tokenizer: simple, extended_errcode=1Same code works fine with FTS3 and FTS4.
FTS5 have three built in tokenizers:
- The unicode61 tokenizer, based on the Unicode 6.1 standard. This is the default.
- The ascii tokenizer, which assumes all characters outside of the ASCII codepoint range (0-127) are to be treated as token characters.
- The porter tokenizer, which implements the porter stemming algorithm.
It seems, that default "simple" tokenizer was renamed to "ascii" in FTS5 and it isn't a default anymore.
For some functions, like Trim() the easiest is to create an overload
Yea, it easy, but wait: [DCC Error] SynCommons.pas(24041): E2251 Ambiguous overloaded call to 'Trim'
SynTrim is a good name for this function that will fix all troubles.
We can just use fully qualified names for procedures and functions. for example: `unit1.procedure1`
Your example is not relevant because mORMot 2 uses long units names. In my opinion, using prefixes is preferable.
mormot.core.base.TrimU()SynTrim()In this blog-post you wrote:
It is also mandatory that you declare the record as packed.
Otherwise, you may have unexpected access violation issues, since alignement may vary, depending on local setting, and compiler revision.
So, if I will use fixed alignment {$A8} and the same compiler to Save/Load records, can I not use "packed"?
I'm asking because in my simple test it works fine without "packed". Tested on Delphi 10.3.3 (x32/x64).
Documentation says:
- will handle packed records, with binaries (byte, word, integer...) and
string types properties (but not with internal raw pointers, of course)
so is it mean that only packed records supported?
And the same question about RecordSaveJson - does it work with all records or packed only (documentation says nothing)?
Is this topic still about FastMM5?
Embarcadero C++ 10.2 "Tokyo" Compiler and Command-line Tools (Win32 only) direct link:
http://altd.embarcadero.com/download/bc … BCC102.zip (45.2Mb)
The Embarcadero 10.2 Tokyo C++ compiler is a free, Clang-based compiler for 32-bit Windows. The download includes a number of other tools, as well as the Dinkumware STL and headers and import libraries required to build both command-line and GUI Windows applications.
But the DLL idea probably wouldn't work because I have a feeling the graphical code cannot run in anything but the main thread.
You can call functions from the main thread, no matter where they are. So, the dll idea should work.
I wold prefer explicit form: SysUtils.Trim(...)
At first, you should answer the question: what is the "large blob" means in your case? Is it 1k, 100k, 1M, 100M?
After this you can go here: https://www.sqlite.org/intern-v-extern-blob.html and check if your case is "green" (than you can use blobs) or "red" (then use file system).
mpv
Problem with linking sqlite3.obj compiled with modern Free C++ compiler (Clang based) under Win32. At that moment mORMot uses classic compiler from Community Edition which ships with commercial use limitations: https://www.embarcadero.com/free-tools/ccompiler
This errors says that you should continue refactoring SynSQLite3Static.pas if you want to get it work.
Open SynSQLite3Static.pas and add constant prefix:
{$ifdef MSWINDOWS}
{$ifdef CPU64}
{$L sqlite3.o} // compiled with C++ Builder 10.3 Community Edition bcc64
{$else}
{$L sqlite3.obj} // compiled with free Borland C++ Compiler 5.5
const _PREFIX = '_'; // compiled with modern C++ Builder Free compiler (Clang based) <------ add this
{$endif}
{$else}This will fix name underscore problem and than maybe compilation will be successful.
In your obj file this functions named as _CodecGetReadKey, _CodecGetWriteKey etc. so this is naming convention problem. Maybe there is missing some compiler switches?
Explain, what is not working for you?
Did you try to use this bat file: https://github.com/synopse/mORMot/blob/ … ite3/c.bat
@echo off
attrib -r sqlite3.obj
del sqlite3.obj
rem set bcc=\dev\bccXE7
set bcc=d:\dev\bcc
%bcc%\bin\bcc32 -6 -Oi -O2 -c -d -u- sqlite3.c
attrib +r sqlite3.obj
pauseor take this (for win64) and adopt it for win32.
And now, Delphi (2007, 10.3.2) can't compile this unit because of missing initialization section.
Fix:
initialization
// empty
finalization
if PtrInt(curl.Module)>0 then begin
curl.global_cleanup;
FreeLibrary(curl.Module);
end;1. Add some missing/renamed constants to the TCurlOption: https://github.com/synopse/mORMot/pull/243
Main issue of this constants is to be able to use modern progress callback function (CURLOPT_XFERINFOFUNCTION) as recommended in documentation https://curl.haxx.se/libcurl/c/CURLOPT_ … CTION.html
2. Let pass libcurl dll name as a parameter: https://github.com/synopse/mORMot/pull/245
I would prefer to use the same library name for Win32 and Win64 versions with my application.
LibCurlInitialize can be refactored with additional variable wich solves the problem. I will create pull request with fix tomorrow.
I'm talking about this function:
function CurlIsAvailable: boolean;
begin
try
if curl.Module=0 then
LibCurlInitialize;
result := PtrInt(curl.Module)>0;
except
result := false;
end;
end;Now, consider situation when thread A and B calls it at same time (library is not initialized yet).
1. Thread A compares curl.Module with zero and call LibCurlInitialize.
2. Thread A enters into critical section inside LibCurlInitialize and load dll into memory (call LoadLibrary), now curl.Module <> 0
3. Thread A interrupted halfway (no any GetProcAddress called yet) and Thread B start its work
4. Thread B compares curl.Module with zero and return True
5. Thread B try access to the any function (for example, curl.easy_init) and fails with AV!
To fix this bug we need to use temporary variable for library handle instead immediately writing to curl.Module.
curl.Module should be written only at the exit from LibCurlInitialize when initialization is fully complete.
/// low-level libcurl library file name, depending on the running OS
LIBCURL_DLL = {$ifdef Darwin} 'libcurl.dylib' {$else}
{$ifdef Linux} 'libcurl.so' {$else} 'libcurl-x64.dll' {$endif}{$endif};What about Win32 platform? Is it no longer supported?
Thank you.