SQL Server¶
Microsoft SQL Server is a full-featured relational database for enterprise applications. Trysil targets SQL Server 2012 or later: both SELECT NEXT VALUE FOR for the sequences and OFFSET n ROWS FETCH FIRST m ROWS ONLY for the paging were introduced in that version, so on 2008 R2 the first insert fails.
Unit: Trysil.Data.FireDAC.SqlServer
Delphi Edition: Enterprise
Warning
The SQL Server driver requires the Enterprise edition of Delphi. It is not available in the Community edition.
Setup¶
OS Authentication (Windows)¶
uses
Trysil.Data.FireDAC.SqlServer;
TTSqlServerConnection.RegisterConnection('Main', 'ServerName', 'DatabaseName');
LConnection := TTSqlServerConnection.Create('Main');
try
LContext := TTContext.Create(LConnection);
try
// Use the context...
finally
LContext.Free;
end;
finally
LConnection.Free;
end;
Database Authentication (SQL Login)¶
TTSqlServerConnection.RegisterConnection(
'Main', 'ServerName', 'Username', 'Password', 'DatabaseName');
LConnection := TTSqlServerConnection.Create('Main');
Encryption¶
Trysil says nothing about encryption unless you ask it to, so the ODBC driver
applies its own default: Driver 18 encrypts, Driver 17 and older do not, so on
a machine where Driver 18 is installed the default in force is Encrypt=Yes. Until
2.0.0 Trysil wrote Encrypt=No on every connection, which cancelled Driver
18's default and left the traffic between application server and database in
clear with no way to opt out.
Which generation is in use is not Trysil's decision either. FireDAC picks the
ODBC driver through TFDPhysMSSQLDriverLink.ODBCDriver, which Trysil never
sets: the setter is there, on TTSqlServerConnection.Driver, and nothing in
the library calls it. Read the machine's installed drivers before assuming
which default applies.
To decide instead of the driver, set it before registering the connection:
uses
Trysil.Data.FireDAC.SqlServer;
TTSqlServerParams.Instance.Encrypt := TTSqlServerEncrypt.Yes;
TTSqlServerConnection.RegisterConnection(
'Main', 'ServerName', 'UserName', 'Password', 'DatabaseName');
TTSqlServerEncrypt |
Emitted | Meaning |
|---|---|---|
DriverDefault |
nothing | The ODBC driver decides. The default |
Yes |
Encrypt=Yes |
Encryption required |
No |
Encrypt=No |
Encryption off, explicitly |
Both values are read when a connection is registered, and Encrypt does not
overwrite a parameter the caller already wrote: if the TStrings you pass to
RegisterConnection already carries Encrypt, that is what is used, and the
process-wide value is ignored for that connection. The lookup is by name and
case-insensitive, so encrypt= counts too.
They are one setting for the process, not one per connection: a host that connects to several SQL Servers applies the same choice to all of them. Set them at start-up, before the first registration - including in a multi-tenant host, where registrations keep happening at runtime, one per tenant on its first request, on a request thread.
TTSqlServerTrustServerCertificate |
Emitted | Meaning |
|---|---|---|
DriverDefault |
nothing | The ODBC driver decides. The default |
Yes |
ODBCAdvanced=TrustServerCertificate=yes |
The certificate is not validated |
No |
ODBCAdvanced=TrustServerCertificate=no |
The certificate is validated, explicitly |
Encrypt=Yes needs a certificate the client trusts
Requiring encryption against a self-signed certificate fails
validation, which is the reason Encrypt=No was hardcoded in the first
place. TrustServerCertificate=yes waives the check, and it is not an
ODBC keyword FireDAC knows: the connection string is built from a closed
list of keywords per driver, TrustServerCertificate is not in the MSSQL
one, and the string does not appear anywhere in the FireDAC sources. A
parameter outside that list is dropped without an error, so writing it
as a plain connection parameter does nothing at all.
What reaches the driver is ODBCAdvanced, which FireDAC appends to the
connection string verbatim, outside the keyword filter. That is what the
tri-state emits, and it is the one setting here that appends instead of
yielding: ODBCAdvanced is a single string carrying many settings, so if
the TStrings you pass already has one, ours is joined to it with a ;
rather than skipped. Running the same registration twice does not append it
twice.
Setting TTSqlServerConnection.Driver.ODBCAdvanced works too, but only
before the driver is loaded, which in practice means before the first
connection is opened: the driver reads that value once, in InternalLoad.
The per-connection value is read at every open and takes precedence.
Connection Pooling¶
Connection pooling is recommended for server applications to avoid the overhead of opening and closing a connection for every context a request builds:
Transactions¶
The driver never sends BEGIN TRANSACTION. StartTransaction turns the ODBC
autocommit attribute off, which on SQL Server means implicit transactions:
the server opens one on the first statement that follows, and Commit and
Rollback are the ODBC SQLEndTran call rather than statements of their own.
Two consequences worth knowing before you read a profiler trace or a
@@TRANCOUNT of your own:
- A
BEGIN TRANSACTIONyou are looking for will not be there, andSELECT @@TRANCOUNTinside a Trysil transaction is itself a statement that can open the implicit transaction it then counts, so it is not a way to ask whether the previous statement left one open. SQLEndTransucceeds whether or not a transaction is open, so a rollback after the server has already ended the transaction - a deadlock victim, an error underSET XACT_ABORT ON- is not refused, and the connection is not left believing it is in a transaction it no longer has.
Sequences¶
SQL Server uses database sequences for primary key generation. Create the sequence in your database schema:
Map it to the entity:
[TTable('Persons')]
[TSequence('PersonsID')]
type
TPerson = class
strict private
[TPrimaryKey]
[TColumn('ID')]
FID: TTPrimaryKey;
// ...
end;