You are not logged in.
Arnaud,
Is there an option to get over the currently forced logging and let us handle the errors?
Both Commit and RollBack are procedures, so do not return any error boolean flag.
Perhaps it is an issue, but their execution is expected to continue.EngineExecute returns an error boolean flag.
In all case, any Exception would be logged on the server side.
Found another place that also eats the sqlite errors silently:
TSQLRestServerDB.EngineExecute();
Begin
...
{$ifdef WITHLOG}
DB.Log.Log(sllError,'% for %',[E,aSQL],self);
{$else}
LogToTextFile('TSQLRestServerDB.EngineExecute: '+RawUTF8(E.Message)+#13#10+aSQL);
{$endif}
...
end;I think when withlog is not defined, just call raise, give our programs the chance to get the exact error and handle accordingly. Handling errors with exceptions, instead of IF statements, are a commonly accepted concept, IIRC.
Arnaud,
Why the sqlite3 exceptions are eaten in TSQLRestServerDB.Commit, as shown below?
procedure TSQLRestServerDB.Commit(SessionID: cardinal=1);
begin
inherited Commit(SessionID); // reset fTransactionActive + write all TSQLVirtualTableJSON
try
DB.Commit;
except
on ESQLite3Exception do
; // just catch exception
end;
end;I think there are many cases we need the flexibility of being able to catch the sqlite 3 exceptions and handle accordingly.
Arnaud, this is not a real client/server app, but a single EXE USING TSQLRestClientDB, so it really need to handle the actual sqlite server. Do you think if I switch to TSQLRestServerDB will fix the problem?
Arnaud,
as per this discussion http://synopse.info/forum/viewtopic.php?id=1905
the URI methods doesn't return the actual database error, which we need.
additional info, My derived TransactionBegin() is protected with a TOmniMREW (mulit-reader-exclusive-writer) lock, like this:
function TDataStoreClient.TransactionBegin(aTable: TSQLRecordClass; SessionID:
cardinal): boolean;
begin
//if the db server is in the same process, we protect the writing operation from
//being executed by multi threads.
if IsServerInSameProcess then
gAppScopeDbLock.EnterWriteLock; //this is an global lock
result := inherited TransactionBegin(aTable, SessionID);
end;
procedure TDataStoreClient.Commit(SessionID: cardinal);
begin
try
inherited Commit(SessionID);
finally
if IsServerInSameProcess then
gAppScopeDbLock.ExitWriteLock;
end;
end;Any logical problems in my derived TSQLRestClientDB class?
Arnaud,
I've noticed many times there are .sqlitedb-wal and .sqlitedb-shm leftover files. I use TSQLRestClientDB, I do have WAL enabled, with the intention of getting maximum stability; I do have have multiple threads starting/commiting transactions.
Any help would be appreciated!
edwinsn wrote:If you use SQLite as the backend databae, SQLite itself doesn't support renaming a field.
Assume you have TSQLOldTable, now create a new class called TSQLNewTable with the new field definitions, call CreateMissingTables(); Then read old records from TSQLOldTable and add to TSQLNewTable;
drop tale TSQLOldTable.
Then change all references to TSQLOldTable in your code to TSQLNewTable.
Very thanks!
As I am new to the framework, do I have to copy the table by looping read/write progress, or there is a more effective way provided from the framework?
Maybe execute a raw "insert into...select" SQL statement using EngineExecute() is faster and less of a hassle. Just an idea, I don't remember the details, please google yourself.
Could you please create a ticket?
If you use SQLite as the backend databae, SQLite itself doesn't support renaming a field.
Assume you have TSQLOldTable, now create a new class called TSQLNewTable with the new field definitions, call CreateMissingTables(); Then read old records from TSQLOldTable and add to TSQLNewTable;
drop tale TSQLOldTable.
Then change all references to TSQLOldTable in your code to TSQLNewTable.
I believe before adding a TSQLrecord, you cannot get the ID, and this how sqlite works.
Arnaud,
I use TSQLRestClientDB, which means the client and the server are the same object.
Here is a situation, when the app's writing to a sqlite database file, suddenly the drive where the database file's located become unavailable.
When in IDE debug mode, I get ESQLite3Exception with message 'disk I/O error', and this is what I want since the exception tells what's happening.
But when running out of the IDE, I have to read the LastErrorCode and LastErrormessage of TSQLRestClientDB,and the error code is 400, and the error message is JSON string, something like {"ErrorCode": 400, "ErrorText": "Bad Request"}. Obviously, the "Bad Request" error message is too generic to understand and handle.
so my question is, how to let my try...except...end statement catch the original ESQLite3Exception exception with the message 'disk I/O error'?
Thanks!
Arnaud, you are welcome! You have done a great work!
Thanks Arnaud, the problem's fixed. I guess fmShareDenyNone doesn't ensure thread-safety, I guess the problem was that, Thread B trying to open the same .mab file while thread A hasn't done its opening of the very same file.
done: http://synopse.info/fossil/tktview/ea4e … 3d017e997f
What would be the idea of fixing it?
Hi Arnaud,
I got an error message something like "Cannot open myProgram.mab file, another program is using it" in my program, where multiple threads has their own TSynLog objects.
My derived TEyLog:
constructor TEyLogger.Create(const aLogFilePath: string; const aBufSize:
Integer = 4096);
begin
FSpecifiedLogFile := aLogFilePath;
FSpecifiedBufferSize := aBufSize;
inherited Create(nil);
//by default accept all levels of logging
Self.Family.Level := LOG_VERBOSE;
Self.GenericFamily.Level := LOG_VERBOSE;
end;
procedure TEyLogger.CreateLogWriter;
begin
if fWriterStream = nil then
begin
fFileName := FSpecifiedLogFile;
if not FileExists(fFileName) then
TFileStream.Create(fFileName, fmCreate).Free; // create a void file
fWriterStream := TFileStream.Create(fFileName, // open it with read sharing
fmOpenReadWrite or fmShareDenyWrite);
end;
if fWriter = nil then
begin
if FSpecifiedBufferSize < 64 then
FSpecifiedBufferSize := fFamily.BufferSize;
fWriter := SynCommons.TTextWriter.Create(fWriterStream, FSpecifiedBufferSize);
end;
end;Hi guys, I didn't dig deep into your discussions here, so please forgive me if what I'm posting here is irrelevant. So today I came across a web service that allows you creating a web API by uploading an Excel spreadsheet plus some point-and-click operations, very simple and cool, I'm wondering if this will give Arnaud some inspiration.
For example, I uploaded an spreadsheet like this:
Order No Order Date Product Id License Key
1 Today p01 abcd-efg
2 2014/7/15 p02 hijk-lmno
3 2014/7/16 p03 erty-oiuyClicked OK, and I got this https://sheetlabs.com/#/services/doc/INNO/test#examples
Yes, it should be sufficient in most cases...
@ab, I understand you now! Sounds like a good idea, but I still have one concern, according to the comment about RecordRef, it seems that the max record ID will be 4294967295(maxUInt) shr 6 = 67,108,863 on win32 platform, right?
@ab, could you give a very simple example of your idea? I couldn't understand how to define a TSQLRecordFTS4 child class utilizing the RecordRef you are talking about, especially how to set the rowid value for a new FTS record...
My current record definition:
TSQLFTSDoc = class(TSQLRecordFTS4Porter)
private
FKind: String;
FSrcRecId: Integer;
FContent: String;
FDate: String;
published
/// the kind of data, eg 'order', 'contact', etc. usually matches the
/// corresponding table name.
property Kind: String read FKind write FKind;
/// the ID of the original record in the original table in the origiinal DB.
/// When we have 'Kind' and 'SrcRecId', we'll be able to locate the original record.
property SrcRecId: Integer read FSrcRecId write FSrcRecId;
property Content: String read FContent write FContent;
/// Human readable date, such as "2014 may 1", please note the spaces in the date string,
/// because we want to utilize the sqlite FTS search query language, so that the user can enter
/// 'date:2014' and search all data within the year of 2014.
property Date: String read FDate write FDate;
end;Note: With is approach, with the explicit Kind (tableName) and SrcRecId fields, I can write joined SQL easily...
ab, There was not backup process, and I did not call the sqlite engine directly, everything goes through the TSQLRestClientDB methods. PS, the WALMode is enabled.
And I couldn't catch you about your question: "is there anything outside basic CRUD operation within the ORM methods?"
The error happened only once, I'll report if I'll get any new details.
Store them as integer, not as string.
I haven't tried, but since sqlite document says a FTS table can only contain string fields except for the rowid, I thought I can have string fields in the TSQLRecordFTS4 decedents. But it's great to know I can also have Integers! How about TDateTime?
For the 'source table', I'll use table names (strings), in order to take advantage of the ":" FTS search operation, for example:
table:orderWill search only orders, but not other kind of data.
Arnaud,
I got this error in my multi-threaded program, when it happened there were multiple threads reading the DB. I use
AcquireWriteMode := amUnlocked;since you previously suggested the unlocked mode is still thread-safe, I'm not sure if it has anything to do with the problem. Let me know if you need any other details.
Note, the stacks bellow is copied at the second debugger break, I forgot to copy the stack trace after the first debugger break.
---------------------------
Debugger Exception Notification
---------------------------
Project MyEXE.exe raised exception class ESQLite3Exception with message 'library routine called out of sequence'.
---------------------------
Break Continue Help
---------------------------
SynSQLite3.sqlite3_check(63587656,21)
SynSQLite3.TSQLRequest.Prepare(63587656,'SELECT ID...')
SynSQLite3.TSQLStatementCached.Prepare('SELECT ID...')
mORMotSQLite3.TSQLRestServerDB.GetAndPrepareStatement('...')
mORMotSQLite3.TSQLRestServerDB.MainEngineList('SELECT ID...',False,nil)
mORMot.TSQLRestServerURIContext.ExecuteORMGet
mORMot.TSQLRestServerURIContext.Execute(execORMGet)
mORMot.TSQLRestServer.URI($D5CFC2F)
mORMotSQLite3.TSQLRestClientDB.InternalURI($D5CFC2F)
mORMot.TSQLRestClientURI.URI('root','GET',$D5CFCD0 {''},nil {''},$D5CFCEC {'SELECT ID...'})
mORMot.TSQLRestClientURI.ExecuteList((...),'SELECT ID...')
mORMot.TSQLRest.MultiFieldValues(TSQLMail,'ID...')
mORMot.TSQLRest.MultiFieldValues(TSQLMail,'ID...',(...))Thanks for the help. You are right, in additional to the source record ID, I also have to store the table ID in order to be able to retrieve the source record at a later time.
I just checked the document, looks like it has some restriction on using TRecordReference, I'd better off store 'source record ID' and 'source table ID' (define as enum) as strings separately in the FTS4 record (TSQLRecordFTS4).
Thank you, things are clarified now.
Hi Arnaud,
Sqlite generate new rowids for each table separately (uses the internal table called sqlite_sequences), but if I want to use a unified rowid sequence, I mean, no rowids in tableA will appear in TableB or tableC, all rowids are unique in the scope of the entire DB (with a few exceptions). What is the simple and reliable way of doing that that you would suggest?
I need to do that, because I want to index data from multiple tables into a single FTS table, and use the original ID from the source tables as the rowid in the FTS table.
Thanks!
Hi MPV,
It's great! Thanks for the help. I'm not trying to build the dll in the near future, just wanted to get things clarified ![]()
Thank you guys!
I checked the BuildInstruction.md file, there is only one thing not clear to me - what program to use to apply those .patch files? Thanks.
Thanks Ab.
Just set Scaled := False for all the forms, then VCL will not do its **incomplete and inaccurate** scaling.
If I do improve SynDbExplroer I'll send you the source.
@ab, thanks for the help.
SynDBExplorer has some font displaying problem, I guess simply disabling form scaling will fix the problem, I'll try later, check this screenshot.
http://screenpresso.com/=Fuwof
And I checked SynDbExplorer again just now, apart from the problem mentioned above, its looks like it's good and should fit most of my needs.
I'd like to enhance it and contribute back the changes, but I use XE4 only, do I have to care about compatibility with other versions of Delphi and FPC?
Back to the collation issue, on the other hand, I want to make sure, if I change the default collations back to the sqlite built-in ones, will there be any side effects?
@mpv, welcome back! And great to hear that. And yes I already know Ternjs, which is written by an amazing developer!
Arnaud,
Mormot uses the custom ISO8601 collation for datetime fields, but I want my app to use sqlite built-in collations only to avoid the incompatibility with external sqlite tools - we always needs those tools during the development process, right?
After reading the manual, I tried the following and recreated the database file, but it seems that the collations doesn't change, any hint? I'm sure the TDateTime fields still use ISO8601, but not sure about unicodeString field yet since I've not tested it. I use XE4 32bit.
1 - all my record objects has a bass class called TSQLBaseModel.
2 - and in this TSQLBaseModel class I have overridden the InternalRegisterCustomProperties() method will looks like:
class procedure TSQLBaseModel.InternalRegisterCustomProperties(Props: TSQLRecordProperties);
begin
Props.SetCustomCollationForAllRawUTF8('NOCASE');
SetCustomCollationForAllDateTimeFields(TSQLRecordPropertiesHacker(Props), 'BINARY');
end;And SetCustomCollationForAllDateTimeFields() is defined as:
class procedure TSQLBaseModel.SetCustomCollationForAllDateTimeFields(const
aProps: TSQLRecordPropertiesHacker; const aCollationName: RawUTF8);
var i: integer;
begin
if aProps = nil then
Exit;
if aProps.Fields.Count>length(aProps.fCustomCollation) then
SetLength(aProps.fCustomCollation,aProps.Fields.Count);
for i := 0 to aProps.Fields.Count-1 do
if aProps.Fields.List[i].SQLFieldType=sftDateTime then
aProps.fCustomCollation[i] := aCollationName;
end;When FPC support is fully implemented, I believe mORMot will bring FPC/Lazurus to the next level of web development, maybe like RoR to Ruby ![]()
Sounds great, thanks ab.
Hello Arnaud,
I use a main sqlite DB to store data, and now I need to full-text index those data, I want to put the FTS tables in a separated sqlite DB for good reasons including reduce main DB size.
So what's the proper way of doing this? I assume I should use a separated TSQLRestClientDB instance pointing to the separated sqlite DB, right? Or use ATTACH DATABASE and how?
Thank you in advance and I wish you have a nice weekend ![]()
AB, after reading some posts in the forum, I realized TSQLRestClientDB should be used instead of TSQLRestServerDB, since the former will allow me to switch to HTTP protocol in the future without any changes to be made?
If I'm right, may I ask, does TSQLRestClientDB have the same thread-safety?
Thanks and I wish you have a nice weekend ![]()
@ab, would you contact mpv via e-mail ask for the source code of the DLL which he promised to publish it? I think it's a key aspect to keep the JavaScript part of the mORMot framework live and going?
Arnaud, You are awesome!
I'll be much easier to navigate the doc.
PS, I suggest Sumatra PDF reader, it's lightweight and fast, contains everything I need but nothing unneeded as a reader, much like Foxit reader in its earlier days.
great! Performance is not a big problem in my specific situation. Thank you.
Hi Arnaud,
The DCPcrypt2 encryption library I'm using have problems in 64bit target, So I have to resort to something else, the first thing I am looking at is SynCrypto.pas.
From the comments in the header of the unit, I can see it compiles under 64bit, however, can I assume its 64bit stability is at the same level of 32bit?
Thanks.
Ab,
I get to this page by googling: http://synopse.info/fossil/finfo?name=SynCrypto.pas
And it's really useful since I don't have a Fossil client program.
However, through your website I failed to find the link that 'file history' links like that, can you help ![]()
I'd like to report that, it's so much faster!
Oh! My bad! Sorry!
As per our conversatin, since I changed to TSQLRestServerDB from TSQLRestClientURINamedPipe, and didn't comment out all calls to .Free(), thus causing the global TSQLRestServerDB instanced got freed unexpectedly, and the error I described above is due to accessing a already destroyed object...
Sorry for have wasted a little time of your, Arnaud ![]()
Anyways, I still want my try...except...end block to catch all internal exceptions of TSQLRestServerDB.
ab, yes, the pdf is huge, forget about it. but I still strongly suggest you to put the sqlite3fts3.obj file to the repository ![]()
ab, one more question, it seems that my try...except...end block can't catch the exception - the exception raises only in the DELPHI IDE? And since TSQLRestServerDB doesn't have the LastErrorException properly like TSQLRestClientURI does, is there a way to catch those exceptions? Thanks.
ab, I must be doing something wrong - from the above conversation my understanding is that multiple threads can use the **same** TSQLRestServerDB object to do both writing and reading safely. However, I encountered this error:
Project myApp.exe raised exception class $C0000005 with message 'access violation at 0x009854d4: read of address 0x00000000'.I was running the program in the IDE, and the debugger stopped it at:
function TSQLModel.GetTableIndex(aTable: TSQLRecordClass): integer;
begin
...
// manual search e.g. if fModel[] is not yet set
for result := 0 to fTablesMax do
if Tables[result]=aTable then //Edwin: the breakpoint was here
exit;And the stack trace:
mORMot.TSQLModel.GetTableIndex(TSQLMail)
mORMot.TSQLModel.GetTableIndexExisting(TSQLMail)
mORMot.TSQLRest.Add($852D2D0,True,False)At that breakpoint, I checked the value of fTablesMax and it was: 7602286.
Also:
Watch Name Value
length(tables) Access violation at 0040CB04 accessing FFFFFFFDShould I create a ticket, or am I doing something wrong? I'm using the nightly build downloaded several hours ago, and I'm sure there is only one single instance of TSQLRestServerDB in the program.
@ab, there is something I don't understand, why amUnlocked is still thread-safe? I checked the source code, in amUnlocked mode, there is no criticalSection protection?
Re named pipe improvements, I'm afraid I've not got a level that I can have any good suggestion for it ![]()
Thanks Arnaud, it's so great to know that, TSQLRestServerDB is so much faster than TSQLRestClientURINamedPipe! I'll try it tomorrow!
I'm using the snapshop of the trunk few weeks ago, I'll download the latest version tomorrow!
If the "clients" are in the same process, you should perhaps not use Named Pipes, but direct call of the TSQLRestServerDB instance. It will be as fast as possible, and thread-safe.
Yes, the clients are in the same process, I didn't know we can directly access the same TSQLRestServerDB instance from multiple threads!
Just to be sure, do you mean, if I have a global var:
unit1
interface
...
var
gRestSvr: TSQLRestServerDB;//this is a global variable declared in a unit.
...
//make the writing to the sqlite3 db runs in a single background thread.
gRestSvr.AcquireWriteMode := amBackgroundThread;And in a TThread.Execute() I can call cany methods of gRestSvr?
gRestSvr.anyRestServerMethod();// this code is in a thread other than the main thread.If yes, that'll be awesome! Can you confirm?
Hello Arnaud,
In my program there is a in-process TSQLRestServerDB server which is responsible for reading and writing the data to sqlite3 db, the clients (in other threads) talk to it via Named Pipes.
But since there are a lot of read operations (in threads) and I feel that the Named Pipe channels are not fast enough, so I am thinking of using additional TSQLRestServerDB instances to read, only read data from the sqlite3 db (no writing, one instance each thread), I believe that'll speed the data reading up.
But my concern is, will there be any side effects with this design? I guess no, but I'd like your comments ![]()
Thanks in advance!
Thanks Arnaud, I did full text search the mORMot source code, but forgot the manual ![]()