Archive for the ‘Delphi’ Category

Doubling Down - Fixing Min/Max() for Doubles

Tuesday, September 17th, 2019

Today I discovered that the built-in overload for Min() and Max() double in the Math unit do not always work as expected when you don’t care about a great deal of precision.  If you search on how to compare floating point types there are many StackOverflow answers, none of which have been adopted over the years by the built-in library.  I have come across this issue of the comparison of floating type values more than once, but keep forgetting not to use built-in functions.  I discovered that the code I was using to determine the range of values was failing on

MyX := Max(MinDouble,0);

MyX was not 0 as I expected because the value is used for a measure of distance for which I only cared about 3 decimal places, so I created a new Math unit with functions that return the expected result.  Hopefully this helps someone else or prevents me from falling into the same hole again.  Naturally these methods should also have overloads for the other floating point types.

unit unMath;

interface

uses
  Math,
  Types;

function Max(const Value1, Value2 :double) :double;
function Min(const Value1, Value2 :double) :double;

implementation

function Max(const Value1, Value2 :double) :double;
var
  relationship :TValueRelationShip;
begin
  relationship := CompareValue(Value1,Value2);
  if (relationship = GreaterThanValue) then
    Result := Value1
  else
    Result := Value2;
end;

function Min(const Value1, Value2 :double) :double;
var
  relationship :TValueRelationShip;
begin
  relationship := CompareValue(Value1,Value2);
  if (relationship = LessThanValue) then
    Result := Value1
  else
    Result := Value2;
end;

end.


Adding a New Attribute to hcOPF

Friday, September 13th, 2019

In order to support a client using SQL Server with replication I needed to add GUID support to hcOPF.  This post is a chronicle of my efforts.

If you’re unfamiliar with ThcAttribute you can breathe a sigh of relief.  It’s analagous to a barebones TField implementation which contains two native Delphi scalar fields used to store the original value of the attribute as read from the object store, and the current value as manipulated by the user.  It also contains two booleans to track whether the value of either field is actually NULL.  Using native Delphi fields of the corresponding type to the database field type minimizes the amount of memory required (a variant is 16 bytes), and still provides functionality similar to the Nullable types found in .NET.  If you’re talking to a database,  NULL <> ” or 0 so you need to provide full support for database NULLs.

First I created a new unit, hcGUIDAttribute and created a corresponding class descending from ThcAttribute.  Since a GUID doesn’t have many valid forms (you can’t convert it to a boolean, integer, or float for example) the number of methods that needed to be overridden is quite small.  Perhaps this is not the best example for what needs to be done when implementing a new attribute type, but it may provide some insight into the framework since ThcAttribute is a fundamental building block.

Since a GUID can really only be represented by a string, variant, or a TGUID we only have to override SetAsString, SetVarValue and their symmetric equivalents GetAsString, and GetAsVariant.

There are three abstract virtual methods on ThcAttribute.  All of these methods are declared as abstract because they must access the native Delphi fields used for the storage of values.  Since only descendants of ThcAttribute declare the private storage fields, these methods must be implemented in descendants, and the code is always the same:

SetOrigAsVariant():  This method sets the original value of the attribute as it was loaded from the database (assuming it ever was).  This method is used by the OriginalValue property to populate the native field FOriginalValue.  Since the framework populates the objects from the database, you may wonder why we need an OriginalValue property in the first place.  The answer is, so we can load single objects we know exist, from the datastore by populating it’s values and calling ThcObject.Read().  The framework also needs to be able to set an object’s OriginalValue as it propagates Primary Keys to child objects without knowing the native datatypes involved.

There are also two virtual methods that must be overriden in all descendants, but are not declared as abstract; ResetModified and UndoChanges.  These methods reset change tracking after the database has been updated, or reset the values back to those read from the database respectively,

Then I added code to ThcAbstractFactory.GetParameterValue for GUIDs and ThcAbstractFactory.PopulateAttributes method to populate the attribute from a database TField.  With a few miscellaneous support methods in the hcGUIDUtils unit  to generate new GUIDs and strip/add brackets to GUID strings it was ready.

Unlike most ORM/OPFs hcOPF supports complex PK/FK constaints and of types other than INT.  The SQL Server system I wrote that required GUID support has been running now for 10 years with minimal issues.

FireDAC TFDScript component - Bug or Feature?

Sunday, September 8th, 2019

When switching a project from using the Spring4D ORM to hcOPF I was forced to change the implementation of the CreateDatabase functionality that was so easy to implement using Spring4D.  Now I am using a TFDScript component with multiple scripts which naturally have to match the MetaData definitions used by hcOPF, but cannot be easily derived from that MetaData.

I dropped a FireDAC TFDScript component in my datamodule and added 3 named scripts:

TFDScript with 3 SQL Scripts

TFDScript with 3 SQL Scripts

Then in my CreateDatabase method I added code to execute each script like the following:

  Script := FDScript.SQLScripts.FindScript('CreateDatabaseContent');
  FDScript.ExecuteScript(Script.SQL);

Much to my surprise only the first script was executed followed by an AV, so I started tracing into the code only to find that the very first line of code in the ExecuteScript() method calls SQLScripts.Clear!  This means you cannot use the SQLScripts collection for any more than a single SQLScript at a time if you make this method call.  So why then would it be a public method?  Why would it even exist?  Why isn’t there an FDScript.ExecuteScriptByName(’CreateDatabaseContent’) method?

procedure TFDScript.ExecuteScript(const AScript: TStrings);
begin
  SQLScripts.Clear;
  SQLScripts.Add.SQL := AScript;
  SQLScriptFileName := '';
  ValidateAll;
  if Status = ssFinishSuccess then
    ExecuteAll;
end;

According to my interpretation of the documentation ExecuteAll should execute all scripts in the SQLScripts collection in the order in which they are defined. Using the FireDACMonitor and tracing through the code showed it only executed the first script (index 0).  As a result, my ugly hack to make the component work they way it should (correct me if I am wrong here) is:

  for I := FDScript.SQLScripts.Count -1 downto 0 do
  begin
    FDScript.ExecuteAll;
    FDScript.SQLScripts.Delete(0);
  end;

Either I am missing something fundamental, or the TFDScript component is badly broken and/or documented. I have created RSP-26131 in case I haven’t lost my mind, so please vote for it.

hcOPF now supports FireDAC

Saturday, September 7th, 2019

In my last post I talked about how unfortunately Spring4D does not provide change notifications so that developers can easily determine if an object graph has been modified and needs to be persisted.  It was primarily for this reason I decided to change the persistence layer in my latest project to hcOPF.  Obviously, I’m more familiar with this ORM, and I know it’s viable because systems I have authored 10 years ago are still being used without issue.  Even though I am using Delphi Rio 10.3.2, with lots of new language features, I don’t have time to thoroughly investigate and learn other alternatives. Change notifications will allow me to implement data auditing, and the validation & binding framework functionality will ensure any data editing is easily crafted.

Unfortunately, IBX no longer works well with Firebird 3.04 even if you can actually find a version for the latest Delphi editions.  I already ran into issues with UIB supporting larger varchar columns so that was not an option.  Instead, I decided to implement support for FireDAC, in part because it had been requested and because my Spring4D implementation with FireDAC proved to be performant.  The implementation was basically a copy/paste/modify of another DAL layer.  It currently does not support StoredProcs, but queries work. More testing and additional unit tests are needed, but it should allow developers to get started using FireDAC.

I also added support for quoted column names which is a Firebird requirement if you use mixed case names.  I’ve been dogfooding it for about a week now, and ironing out a few issues encountered.  I’m pleased with the performance, as hcOPF seems to be faster than my Spring4D ORM implementation.  If I get time I will try to put together some benchmark comparisons.  My only explanation for this would be Spring’s use of RTTI.

FireDAC was added for Rio and then Tokyo. Projects should be easy to back port to other recent Delphi versions. There is one demo specific to FireDAC in the Demos folder with projects for Rio. That should show you how to get started, and it’s essentially the same for all DALs;  create a datamodule, drop a connection and all the hcOPF components on it, link them, and configure your database connection.

FireDAC DataModule Components

FireDAC DataModule Components for a Firebird Database

Spring4D ORM Change Tracking

Sunday, September 1st, 2019

Spring4D’s ORM uses an instance of an IEntityMap to perform change tracking. The default implementation is essentially a threadsafe wrapper around a Dictionary that uses an entitykey of the classname + ‘$’ + the object Id and the Value portion is an array of member values. If an object instance is present in the EntityMap, IsMapped() returns True.

As objects are mapped from ResultSets, they are added to the the EntityMap capturing their values as read from the database. If you want to see what changes were made to the objects you must call GetChangedMembers() which returns a list of attributes for columns that have changed. In effect Spring4D keeps two copies of the data for a given object: the data in the object instance itself and the EntityMap, which is a reasonably compact version of the data as it was originally read from the database result set.

hcOPF takes a different approach, in that each object instance essentially has 2 identical field variables for each attribute and a boolean field to track Nulls. The advantage of this approach is that you can easily test each attribute to see if it’s NULL or has been changed, in user code without testing all of them. Since the implementation does not use RTTI it is likely faster, and hcOPF Attributes surface change notifications. Of course the trade off is larger object instances.

Change Notifications are a very powerful mechanism for both the framework and developer code. They can be used to trigger attribute level validations, provide GUI feedback of modification status and provide for very granular efficient calculations on attributes. In fact these change notifications are the basis for hcOPF’s ability to save an object graph to the database by simply calling object.Save on the root object.  Some frameworks like mORMot have even implemented database auditing tracking.

Sadly, Change Notifications are something missing from the Spring4D ORM. If you want to know if a an object has been Modified you can of course add a FModified boolean field and set it in each property setter after checking if the value being set differs from the current value. It’s a roll your own state tracking that you would expect the ORM to do for you, and it doesn’t take into account situations where the user modifies an object property and then changes it back either by Undoing the change, or changing it back to the original value.

In developer code, change notifications can dramatically simplify logic to save data when the application allows users to modify numerous root objects with the ability to undo changes. It can become quite difficult to determine what method calls are required to save all changes with Spring4D when you don’t know what has been changed in the first place.

Ordering IN()

Friday, August 9th, 2019

This post is not about DoorDash or SkipTheDishes, it’s about using the Where IN() clause in SQL. Yesterday I thought I had discovered a way to order the inclusion of child records since this StackOverflow answer led me to believe that ordering values in the IN() clause will in fact order the resultset in Firebird. Unfortunately, in my testing with Firebird 3.04 this is not the case.

So beware, StackOverflow can be a great source of information, but it’s not always correct. It’s also of course better to explicitly order results rather than rely on the behaviour of a query processing engine so I will be re-writing the Marshmellow code used to generate the query.

Linux Compiler will remain an Enterprise Only Feature

Friday, August 9th, 2019

In case you have not been watching RSP-17195 Marco just closed the ticket yesterday. Apparently EMBT has decided that the Linux compiler will remain an Enterprise edition feature and will not appear in the Pro edition as requested.

I have written before as to why I think this a major marketing mistake, so just briefly, Linux is used by more hobbyist programmers and IoT/SBC developers than most other OSes, and there are many FOSS solutions available. Few people interested in targeting Linux will pay for the Enterprise Edition as they can target a Windows machine and pay the M$ license cheaper than buying an Enterprise edition. It also makes no sense to allow the targeting of Windows and MacOS in the Pro edition, but not the other desktop target. I think this is the same mistake that was made when originally selling the Mobile AddOn, which EMBT later decided to include in Pro in order to limit development costs and increase the user base. Licensing FMXLinux will likely not amount to much of an incentive as my understanding is most developers who have currently licensed the Enterprise edition are writing server apps. Increasing the cost of writing a desktop app will not encourage the uptake of hobbyist or professional developers looking to monetize apps on a platform that users typically prefer FOSS solutions.

Other vendors like REMObjects do not charge additional money for a product edition to target a supported platform. You get all platforms for the same subscription price, and they have now written an RTL and WASM support which makes their platform that much more attractive to Delphi developers. REMObjects also has 64 bit ARM target support today.

.NET cannot be ignored now that .NET Native and Core have been released and are rapidly maturing. Delphi’s “native” compilation is no longer an advantage and the C# code generated tends to be more efficient than Delphi’s various compilers by all accounts.

You can have the greatest product in the world, but if you cannot sell it to a sufficiently large user base, it will not be self sustaining. Marketing decisions like this IMHO will negatively impact the Delphi user base.

Delphi Now Has Linux UI Support

Wednesday, June 26th, 2019

Just announced, FMXLinux has been licensed from KSDev for inclusion in the Enterprise and Architect editions of RAD Studio and related products (Delphi & C++ Builder).  The product is available via GetIt for all current subscribers.

Since I have been following FMXLinux’s development for some time, I immediately installed the licensed version.  Unfortunately, the IDE experience is the same as the trial I previously installed directly from KSDev.

I created a new Multi-device application with tabbed navigation from the wizard.  Looking at the list of target platforms (Android, Mac OS, iOS, Windows) I didn’t see Linux.  Fair enough, the templates on which the application is based do not contain the new UI platform.  Perhaps in a subsequent release since as per Marco’s introduction the product has not been fully integrated into the IDE.

I went to add the new platform to the solution, by Rt. clicking The Target Platforms node in the project manager and it is greyed out.  If I delete a target it is re-enabled, but the list of targets I can add only contains the one I just deleted.  Rt. Clicking on the Application node in Projects window, there is an option “Add Linux Platform”.  Now I get a Linux 64 bit target platform node.

This is covered in Marco’s introduction, but he doesn’t mention that attempting to add a component to my UI doesn’t work.  I can’t add a button to my form until I switch to another target platform, and then switch back so I can actually deploy and test on Linux.  This has been a known issue with the trial, and I was hopeful that such a poor user experience would be fixed now that EMBT has licensed the product.  For now, it’s an issue you will have to work around, and I’m sure it will be corrected in a future release.

While only major Linux distros are currently supported (Ubuntu, RedHat and possibly a couple others) the team at KSDEV are working to support others including the more obscure ones like Elementary OS.  I for one am very pleased that Delphi finally can target Linux with GUI applications making it a complete XPlatform desktop solution for all 3 major targets.

Android - The case of the vanishing file

Saturday, June 22nd, 2019

I’m relatively new to the Android development platform, coming from the Windows desktop. One of the great things about recent editions of Delphi is it’s ability target numerous other platforms. One of the worst things about recent versions of Delphi is it’s ability to target platforms you are not as familiar with. If your use case is atypical, you can quickly find yourself swimming in shark infested waters. You never know what is going to byte you, a bug in the platform, a bug in Delphi RTL or FMX code, a bug in your code, or just your lack of knowledge about the platform.

Today I was trying to figure out why files I was saving to the Documents folder on my Android LG5 were visible from the Android File Manager, but not present when I connected the phone to a Windows, or Ubuntu desktop. Turns out there is a long outstanding bug in Android that google has not even acknowledged. In order to see new files when connecting to a device using the MTP protocol, you may need to re-boot the Android device before the files appear. Indeed once I did so, my files appeared and I could copy them off to my PC. You may want to comment on this motion to get the issue re-opened and addressed.

Hopefully this helps someone else avoid spending a lot of time scratching their head…

JIRA is Down

Thursday, May 16th, 2019

Just a note to EMBT customers, JIRA has been down for several hours now.  It is currently giving the following message

HTTP Status 500 - org.ofbiz.core.util.GeneralRuntimeException: Could not determine database type. (Network error IOException: Connection refused: connect)

With a Java callstack if you like that kind of thing.  EMBT has been advised and I’ve been told EDN may also be affected. Hopefully it will be back up shortly.