The goal of this section is to highlight details about the MSSQL2 driver.
Connection Strings
As of SQL Server 2022 the SQL Server Native Client has been deprecated
by Microsoft. As recommended by Microsoft the object based driver makes
use of the ODBC interface to the server. Since ODBC itself has it's
roots in MS SQL Server, the two approaches were always very similar.
Note that the 32 bit ODBC connection is used by 32 bit programs.
The latest Microsoft ODBC Driver for SQL Server can be found at
https://learn.microsoft.com/en-us/sql/connect/odbc/microsoft-odbc-driver-for-sql-server?view=sql-server-ver17.
The following steps will assume that a recent version of this driver has
been installed on the client computer.
Setting up an ODBC connection, on the client computer, is straight
forward, however it can be daunting when done for the first time.
Option 1a : Create a User DSN (Data
Source Name)
This approach is optional. It is possible to create DSN-less
connections (described below).
- The ODBC Data Source Administrator can be executed by going to
the RUN window (Windows key & R) and entering odbcad32
- Go to the User DSN tab, and click the Add
button
- A list of installed drivers will appear. Select the latest ODBC
Driver for SQL Server that is available. Ideally
version 17 or later. Note that versions after 17 require
that the connection be protected with a certificate.
- Next assign the name, description and server this DSN will be
using. For the movie example a good DSN name might be Movies,
the description might be Driver Kit Example and
the server can be selected from the drop-down or entered directly.
I've found the drop-down to be spotty in detecting SQL Server
Instances, so typing the name in can be more effective. The
pattern is MACHINE\SERVER. On my
local machine this is HULK\SQLEXPRESS.
- The next step is to select an authentication method. The two
most common options are With Integrated
Windows Authentication (aka Trusted_Connection) or With
SQL Server Authentication using a Login ID and password entered by
the user. If you choose the latter then you will be prompted for
an admin Login ID and password.
- Two pages of options follow. These are outside the scope of this
document. The defaults should be fine.
- Click on Finish to finish creating the DSN
Option 1b : Connection String
for DSN Connection
In Clarion, for SQL tables, the
OWNER
attribute contains the connection string. The connection string
contains all the information necessary to connect to the database, and
can also contain settings for the database to use. The standard for
connection strings is a semi-colon (;) separate list of setting=value;
Example;
DSN=DriverKit;UID=sa;PWD=password;Trusted_Connection=No;WSID=HULK;Encrypt=no;APP=MyApp;
In this example the last item is optional. The
APP
setting is especially useful though because it allows the server to
identify the source of the traffic, which in turn makes the logs
easier to filter, which in turn makes debugging and performance tuning
easier.
Option 2 : Using a DSN-less connection
In the above example a DSN (Data Source Name) has been created on the
client computer. This contains the server details, ODBC driver and
database name. To create a connection without using a DSN those items
need to be specified directly in the connection string. For example;
Driver={ODBC Driver 17 for SQL
Server};Server=HULK\SQLEXPRESS;DATABASE=filedriverkit;UID=sa;PWD=password;Trusted_Connection=No;WSID=HULK;Encrypt=no;APP=MyApp;
The driver has been tested against
{ODBC Driver
17 for SQL Server} and
{ODBC Driver
18 for SQL Server}, so one of these should be installed and
used. It is expected that in the future Microsoft will release ODBC
Driver 19 etc, and then those can be used.
Option 3 : Using a Clarion Style Connection String
Clarion has traditionally allowed for a comma separated list for
the MSSQL driver. This was of the form server,database,user,password.
It did allow for limited additional settings (like
Trusted_Connection) but it did not allow many typical settings using
this format. For example it does not let you specify the client
driver to use - it picked one for you.
The new driver automatically converts this form of the connection
string (internally) to
Server=server;Database=database;UID=user;PWD=password
If this form is used, then the driver automatically selects the ODBC
driver to use. It will use ODBC Driver 18 for
SQL Server if it exists, falling back on ODBC
Driver 17 for SQL Server if necessary. One of these two
drivers must be installed for the MSSQL2 driver to work.
In general this comma separated form is discouraged and the more
standard semi-colon separated list is encouraged.
Collations
Collations determine the "alphabetical order" of string fields in the
database.
When you create a new database without explicitly specifying a
collation, SQL Server inherits the collation from the server-level
(instance-level) collation. The server collation itself is chosen
during SQL Server installation — it defaults to a collation based on
the Windows locale of the machine, which for English (US) systems is
typically
SQL_Latin1_General_CP1_CI_AS.
If you are accessing an existing MS SQL database, then it already has
a collation, and you will need to work within the boundaries of that
collation. If you are creating a new database though, it may be
advantageous to specify the collation directly, by setting the
prop:CollationDB before calling the
CREATEDB command.
| Token |
Meaning |
Alternatives |
SQL |
Legacy SQL Server collation rather than a Windows collation. |
Omit |
| Latin1_General |
Locale/language sorting rules |
French, Japanese, Arabic, etc. |
| CP1 |
Code Page 1. (Windows 1252, which is Western Europe) |
CP1252, CP1253
and so on. |
| 100 |
Sort rule version (100 = SQL Server 2008+) |
90 (2005+), no number (legacy) |
| CI |
Case Insensitive |
CS (Case Sensitive) |
| AS |
Accent Sensitive |
AI (Accent Insensitive) |
| SC |
Supplementary Characters support |
Omit if not needed |
| KS |
Kana Sensitive (Japanese) |
Omit if not needed |
| WS |
Width Sensitive (e.g. full vs half-width chars) |
Omit if not needed |
| UTF8 |
UTF-8 encoding (SQL Server 2019+) |
Omit for non-UTF-8 |
Because the install value may be quite old, it is worth
considering the collation for the new database carefully, because a
newer collation may serve the program better moving forward. Changing
the collation of the database, and data in the database, is hard to do
after the database has been created.
A good collation to use (for US and Western Europe) is
Latin1_General_100_CI_AS_SC.
This is a Case Insensitive sort, but is Accent Sensitive so
distinguishes
é from
e.
Some alternatives for Latin1 are in the table below. (This list is not
exhaustive, see your SQL Server documentation for your locale.)
| Collation Prefix |
Countries / Languages |
Notes |
| French |
France, Belgium, Switzerland, Canada |
Handles French accent sorting rules (e.g. é, è, ê) |
| Spanish |
Spain, Latin America |
Traditional Spanish sorting (ñ, ch, ll handling) |
| Modern_Spanish |
Spain, Latin America |
Updated sorting rules; ch and ll no longer treated as single
letters |
| German_PhoneBook |
Germany, Austria, Switzerland |
Phonebook style: ä sorts as ae, ö as oe, ü as ue |
| German |
Germany, Austria, Switzerland |
Dictionary style: ä sorts near a, ö near o, ü near u |
| Greek |
Greece |
This handles the Greek alphabet correctly, including
characters like α, β, γ, δ, ε, and the various accented vowels
used in modern Greek (ά, έ, ή, ί, ό, ύ, ώ). |
| Danish_Norwegian |
Denmark, Norway |
Handles æ, ø, å correctly; these sort after z |
| Finnish_Swedish |
Finland, Sweden |
Handles å, ä, ö correctly; these sort after z |
| Polish |
Poland |
Handles Polish characters (ą, ć, ę, ł, ń, ó, ś, ź, ż) |
| Czech |
Czech Republic |
Handles Czech diacritics (á, č, ď, é, ě, í, ň, ó, ř, š, ť,
ú, ů, ý, ž) |
| Slovak |
Slovakia |
Similar to Czech but with Slovak-specific sorting rules |
CREATE
One major difference in the MSSQL2 driver over the traditional MSSQL
driver is the
CREATE command. The
traditional driver has a very simple
CREATE
command, which does not allow for much control over creating the
table. The documentation goes so far as to say;
Although CREATE is supported, only basic use is
recommended, because it cannot handle all of the features (i.e.,
constraints and relationships) that an actual SQL CREATE TABLE
statement can support. Use of PROP:SQL is preferred in this case.
In contrast to this the object based SQL drivers implement
CREATE,
and by default create a table correctly. It also gives the developer a
mechanism to control the specifics of the table creation.
| Traditional Behavior |
Object Based Behavior |
| If the table exists in the database, and CREATE
is called, then the table is dropped and a new one is created. |
If the table already exists in the database, then a File
System Error (90) Error is set. If replacing the
table is desired do a REMOVE(table)
first. |
| After call to CREATE, prop:sql
returns only the SQL for creating the last index. |
The prop:SQL returned contains
the complete CREATE TABLE, as well
as any CREATE INDEX statements. |
| Creating tables with TIME
fields would fail. |
TIME data type supported. |
DateTime Fields
MS SQL Server supports a
DATETIME (and
DATETIME2) data type. This s a type that combines
a
DATE and
TIME
together into a single column. Clarion does not have a matching data
type, so in order to support this type there are several options.
The common option is to use a
GROUP over
a
STRING. The exact pattern looks like
this;
DateTime
String(8)
DateTimeGroup Group,Over(DateTime)
DateTime_Date Date
DateTime_Time Time
End
This is a very effective approach. The driver recognizes this
structure, and automatically translates the
DateTime
field into a Date component, and a Time component. The program can
then use
DateTime_Date (and
DateTime_Time)
just like a regular
Date and
Time
field.
It should be noted that early Clarion, and early
databases did not necessarily have support for the DATE
and TIME data types. This lead many
programs to use a DATETIME even when they
only wanted the date part or the time part. Doing this has all
kinds of knock-on problems. Since both databases, and also Clarion,
support simple DATE and TIME
fields, these should be used for storing distinct dates and times.
Do not use DATETIME when you only need the
DATE part or the TIME
part.
One limitation with the above approach is that the precision of the
time component is limited to the precision of the
TIME
data type. Since Clarion Time supports hundreds of a second,
this means this approach is fixed at 2 decimal places (ie, hundredths
of a second). By contrast SQL engines allow for more precision than
that - commonly 3 or 7 digits of precision. For this reason it's
possible to create a
STRING(29) or a
CSTRING(30) which maps to a
DATETIME
(or more commonly a
DATETIME2) type on the
backend. The Declaration of the field looks like this;
someDateTime
CSTRING(30),NAME('someDateTime | MSSQLTYPE=DateTime2(7)')
Note the extended name attribute which tells the program that the
backend data type should be a
DateTime2 with
7 digits of precision. The driver will then automatically translate
this string into that type, and vice versa, as data is read in and out
of the program. Having the Date and Time values in a string is fine
for display, but less easy to process as a "date" or "time" value. In
order to extract the date and/or time from the string you need to
extract, and deformat the values. You can use the Clarion
DEFORMAT
and
FORMAT commands for this, or make use
of the StringTheory library (
StringFormat and
StringDeformat classes) to do
the extraction for you.
The format of the string is a date part (
@D010-)
which takes the form
yyyy-mm-dd then a space
separator then a time part (
@T04) which
takes the form
hh:mm:ss followed by a decimal point,
and the fractional seconds. For Example;
2025-09-16 13:51:43.1234
Note that the separator between the date and time parts is a single
space, and a
T is not used. This is in line with how
most databases display the value.
The database does not store the value as a
CHAR,
but rather as a
DATETIME2. So even though
the program sees it as a string, it's not stored as a string.
Driver Options
See Also
Common
Driver Options.
| Option |
Default Value |
Description |
/AUTOINCUSESSCOPEIDENTITY
/AUTOINC
/PREAUTOINC |
n/a |
These settings allow control over how identity fields are
returned to the program after doing an ADD. The new drivers
use none of these options, and hence these settings are not
implemented. The new drivers make use of the OUTPUT
clause in the generated SQL to return both identity, as well
as any (not written) fields with default values. This is
supported in SQL Server 2005 and later. |
/BUSYHANDLING
|
n/a
|
This setting is used by the traditional driver to define the
behavior when multiple threads talk to the same database at
the same time (over the same connection). Since the
connections in the new drivers are thread based, connections
are not shared across threads. Therefore this option no longer
has any meaning. Effectively the new drivers are always in
BUSYHANDLING=2 mode.
|
| /GATHERATOPEN |
|
Not Implemented Yet. The driver behaves as if
/GatherAtOpen=True |
| /HINT |
n/a |
The use case for this property has not been clarified. It
has not been implemented. Developers who have used this
property are encouraged to contact CapeSoft to discuss future
implementation. |
| /LOGONSCREEN |
True |
|
/MULTIPLEACTIVERESULTSETS
|
n/a
|
This is a setting used by the traditional driver, but which
is not applicable to the Obd driver. The function of this
switch was to allow for multiple result sets to be stored in
the server at the same time. Since the new drivers do not use
cursors, no result sets are stored in the server, so this
option has no meaning.
|
/SAVESTOREDPROC
|
n/a
|
Not Implemented. Some prepared statements are cached on the
server, these are automatically removed when the FILE
or VIEW is closed.
|
/TRUSTEDCONNECTION
|
false
|
Trusted connections are a way for the database connection to
use the same credentials (User name) as the User used to log
into Windows. In other words if the user logged into Windows
as "Howard", then it connects to the database using the user
name Howard (and no further password is required.) Obviously a
user Howard has to exist in the MS SQL database list of users
for this to work.
Trusted Connections are usually set by simply adjusting the
owner string. In other words the regular owner string looks
something like this;
server,database,user,password
it becomes
server,database,,;Trusted_Connection=Yes
|
Field Options
The driver supports
Extended Name Attributes
for fields.
To set a field to a specific data type, unique to this driver, use
MSSQL2Type(whatever) where
whatever
is a data type unique to MS SQL Server.
This will override the type set using
SQLType(something)
if it exists.
Example;
paid
Byte,name('paid | sqltype(tinyint) | MSSQL2Type(Integer)')
Field Types
This section discusses types, and type behaviors specific to the MS
SQL Server database.
UNIQUEIDENTIFIER
In Microsoft Sql Server the UNIQUEIDENTIFIER
field type is a 16 byte binary string, which is typically displayed
as a 36 byte hex-encoded string, with 4 hyphens.
If you wish to create a UNIQUEIDENTIFIER
field, you can add the SQLTYPE
attribute to the field declaration in the dictionary. For example;
Fieldname | UUID7 | SQLTYPE(UNIQUEIDENTIFIER)
| DEFAULT=NEWSEQUENTIALID()
When the CREATE function is called this
field will be created as a UNIQUEIDENTIFIER
and in this example the default value of this field (if not provided
during an INSERT) is a call to the SQL NEWSEQUENTIALID() function.
The Clarion Data Type for this field should be a STRING,
and the length set to either 16 or 36. If 16, then the field will be
a binary value in the record. If 36 then the field will be a 36
character, hex encoded value (with 4 hyphens). For example 0E33E2E7-A3BB-CA48-A3D9-DE0D9581C3B4.
Note that when reading and writing
the database the driver determines the SQL Type by inspecting the
database. It is not determined by the SQLTYPE
setting above. That setting is only used for CREATE
command not for the read and write commands. If the type in the
database is a UNIQUEIDENTIFIER then it
maps into the Clarion STRING, even if
there is no SQLTYPE setting in the
dictionary. Again, the string should either be 16, or 36 characters
long.
If the field is set as SQLTYPE(UNIQUEIDENTIFIER)then
it will usually (but not necessarily) have UUID4
or UUID7 set as well. The UUID4
and UUID7 settings are primarily used
as a client-side default when a row is being inserted, and the field
is blank. If the field has a server-side default set ( DEFAULT=NEWSEQUENTIALID()or
DEFAULT=NEWID() ) then the field does
not need to be populated on the client side, and so the UUID4
or UUID7 attributes are redundant. The
server-side function NEWID() generates a
UUID version 4 value. The server-side NEWSEQUENTIALID()
function generates a time-sequenced value, however it is not a
standard UUID version 7 value.
Properties
| Property |
Comment |
| prop:ChildrenAffected |
Not supported by MSSQL. |
| prop:Hdbc |
Returns the current connection handle. As used by the ODBC
API. |
|
|
Multiple Active Result Sets (MARS)
In the traditional MSSQL driver connections to the database (by
default) are shared across threads
[1].
To allow multiple threads to interact with the database at the same
time (over the same connection) the driver offers the option
/MULTIPLEACTIVERESULTSETS=TRUE.
Since the object based drivers do not share connections across
threads, this setting becomes unnecessary. It does no harm setting it,
but it is simply ignored by the driver.
Note 1:
The
/BUSYHANDLING option determines this
behavior. If
/BUSYHANDLING=2 then
connections are not shared between threads.
SEARCH
In order to use the
SEARCH function in
MSSQL Server, a single-component primary key is required.
SUSPENDRELATIONS
This command should be used with care.
With the MSSQL2 driver the
SUSPENDRELATIONS
call applies only to the table itself. Not to the connection or
database as a whole. However, they disable not just the relationship
tests, but also
CHECK constraints (ie custom
validations) and also
UNIQUENESS
checking.
Note that this suspension is applied to the database, for the table.
It thus applies to ALL PROGRAMS, not just the program or thread making
the call. Care should be taken to ensure that
RESUMERELATIONS
is called or the relationships (and other constraints) will remain
suspended indefinitely.
In MSSQL
SUSPENDRELATIONS allows rows to
be added, changed and deleted without relational checks (and without
the other checks described above.) However it does not allow a table
to be dropped, so the
REMOVE command will
still not work on a table if it is referenced by another table.
EMPTY will work, but to drop the table you
first need to also drop the tables, or references in the other tables.
TIME Fields
The
TIME data type in SQL Server allows the
field to declare the decimal precision of the field. This is written
as
Time(n). When
n is 0
then this limits the resolution to seconds, when
n is 1 to
tenths of a second,
n is 2 to hundreths of a second and so
on. Up to seven decimal places are supported (by the database). By
contrast the
TIME data type in a Clarion
structure contains hundredths of a second. This is directly equivalent
to a
TIME(2) field in SQL Server.
If the Clarion program creates the table, using
the MSSQL2 driver then the default field type for Clarion's TIME
is the SQL Time(2). While this is an
uncommon precision for TIME fields in SQL
databases, it is the correct match for the Clarion type.
If the table was created in the database using some other means, then
the most likely precision's to be encountered are
TIME(0),
TIME(3) and
TIME(7).
If these (or any others) are mapped to the Clarion
TIME
type then some of the data may be lost when reading or writing. For
example, if a
TIME(3) value contains
milliseconds, then if it is loaded into a Clarion
TIME
field, only hundredths of a second are loaded. The last digit of
precision is lost.
Equally if a Clarion
TIME field is
written to the database, only 2 digits of precision are written,
regardless of the back end precision. If the back end precision is
less than 2, then the time is truncated before storing.
Unlike the case with strings, if digits of precision are lost when
reading a
TIME value, the driver does
NOT generate a
DataTruncatedError (132)
Error. Rather the value is simply silently truncated.
If a time precision greater than 2 is required, then either a
STRING
field or a
CSTRING field should be
used on the Clarion side. The length should be at least 9 plus the
precision. So a
Time(7) should be 9+7 = 16.
If a
CSTRING is used add 1 for the
terminator.
For example;
EntryTime
String(14),Name('EntryTime | MSSQLTYPE=TIME(5)')
ExitTime
CString(17),Name('ExitTime | MSSQLTYPE=TIME(7)')
TIME Field Defaults
The default value for a time field can be set as a fixed value, like
8:00, or a dynamic value;
now or
utcnow .
If the default is set to
now, then the
local server time will be written into the field if a value is not
supplied. For MS SQL the
sysdatetime()
function is used.
If the default is set to
utcnow then
the UTC time is written into the field if a value is not supplied.
For MS SQL the
SYSUTCDATETIME() function
is used.
EntryTime Time,Name('EntryTime |
Default=now')
ExitTime Time,Name('ExitTime | Default=utcnow')
LunchTime Time,Name('LunchTime |
Default=13:00')
UNIQUEIDENTIFIER Type
In MSSQL the server-side
UUID type is
known as
UNIQUEIDENTIFIER. If the
SQLTYPE() in the dictionary is set to
UUID then it will automatically be translated
to
UNIQUEIDENTIFIER on the server side.
Postgres2 Driver
The goal of this section is to highlight details about the POSTGRES2
driver.
Work In Progress
The following commands are not yet functional;
SEARCH .
Boolean
PostgreSQL has a
BOOLEAN data type. If a
field is marked as
Boolean, then this
type is used.
Paid
BYTE,NAME('Paid | Boolean')
Booleans are always 0 or 1.
The only constraint that is supported is NOT NULL. Any other
constraints declared in the dictionary are ignored.
Delivered REAL,NAME('Delivered | Boolean |
NotNull')
CLONE
The Postgres2 driver groups the options
Driver:Constraints,
Driver:ForeignKeys and
Driver:UniqueKeys
together. If any of these are specified then all of them are applied.
Connection Strings
The native connection string for PostgreSQL is a space separate list
of keyword=value. if the value contains a space then the value must be
quoted, with single quotes. Single quotes, and backslashes in a value
must be encoded as
\' and
\\
respectively. Keywords are case sensitive.
Example
host=localhost port=5432 dbname=filedriverkit
user=bruce password=Bruce1
An alternative format is the URI format
Example
postgresql://bruce:Bruce1@localhost:5432/filedriverkit
The PostgreSQL documentation for the connection string can be found at
https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING.
A complete list of possible keywords is at
https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS.
Option : Using a Clarion Style Connection String
Clarion has traditionally allowed for a comma separated list for
the ODBC driver. This was of the form server,database,user,password.
It did allow for limited additional settings (like
Trusted_Connection) but it did not allow many typical settings using
this format.
The new driver automatically converts this form of the connection
string (internally) to
host=server dbname=database user=user
password=password
In general this comma separated form is discouraged and the more
standard space separated list is encouraged.
Collations
Collations determine the "alphabetical order" of string fields in the
database.
Collations in PostgreSQL get really complicated, really quickly. Going
into every nuance is beyond the scope of this documentation (chat to
your AI if you're confused.)
Your context will play a big part in what you can do here. If
you are connecting to a database that already exists, a database owned
by another system, then your flexibility here is limited, and
ultimately your program will respond to the database settings as they
are. However if you are creating the database yourself (perhaps using
the
CREATEDB
command or a SQL script) then you get a one-time opportunity to do it
right. After the database has been created, the default cannot be
changed.
By default, when a database is created in PostgreSQL, it queries the
Operating System to get the default text collation. This is a poor
choice, but for historical reasons, and backward compatibility, it
still does this. It is a poor choice because the behavior of the
database (and hence the behavior of your program) can vary (quite a
lot) between one deployment and another. For example the OS collations
suggested by Windows and Linux are quite different. Once set, the
collation cannot be changed (even if the OS locale changes.) Worse,
the database collation may not even be available if the Operating
System changes (such as moving the server from Linux to Windows or
vice versa.)
Therefore, when creating a new database, it is better to use a
PostgreSQL collation, not the OS collation. These are known as
ICU
collations. A typical SQL statement to create a database would thus
look something like this;
CREATE DATABASE somedatabase
TEMPLATE template0
ENCODING 'UTF8'
LOCALE_PROVIDER = icu
LC_COLLATE 'en-US-u-ks-level2'
LC_CTYPE 'en-US-u-ks-level2'
ICU_LOCALE = 'en-US-u-ks-level2';
| ICU
Strength |
Level |
Case-sensitive? |
Accent-sensitive? |
Description |
| PRIMARY |
1 |
No |
No |
Only base letters matter (a = A = á) |
| SECONDARY |
2 |
No |
Yes |
Accents matter, case ignored (a = A < á) |
| TERTIARY |
3 |
Yes |
Yes |
Accents + case matter (A < a < Á) |
| QUATERNARY |
4 |
Yes |
Yes |
Rare, punctuation and variants considered |
| IDENTICAL |
5 |
Yes |
Yes |
Tie-breaker on code points |
The locale in the above script (
en-US) is not
limited to US characters. It encompasses all unicode characters.
However different countries have some slightly different rules when it
comes to where "international" characters exist when sorting
"alphabetically". For example;
| ICU Locale |
Country / Language |
Notes on Sorting & Differences |
Typical Use Cases |
en-US |
English (US) |
Accents mostly ignored; a < b < c; ä
sorts after a (treated as variant of a) |
General English text, US-centric apps |
en-GB |
English (UK) |
Similar to en-US; minor differences in ordering punctuation
or symbols |
UK English content |
de-DE |
German |
ä sorts as ae (after a),
ß as ss; accents considered;
case-insensitive by default at level 2 |
German text, names, dictionaries |
fr-FR |
French |
Accents matter; é after e; ç
after c; secondary level for case |
French names, dictionaries |
es-ES |
Spanish |
ñ is considered a separate letter after n;
accented vowels distinguished |
Spanish text, sorting names alphabetically |
it-IT |
Italian |
Accents matter (à, è, é)
but sorted close to base letter; ch not treated
specially |
Italian text, names |
pt-PT |
Portuguese |
ç after c; accented vowels sorted
with base letters; ã and õ handled |
Portuguese text |
sv-SE |
Swedish |
å, ä, ö are
distinct letters sorted after z;
case-insensitive at level 2 |
Swedish text, names, dictionaries |
nl-NL |
Dutch |
ij often treated as separate letter; accented
letters handled with base |
Dutch sorting |
da-DK |
Danish |
æ, ø, å are
distinct letters sorted after z |
Danish dictionaries and names |
Most PostgreSQL installs will include all of the above, and many other
ICO collations. However, especially on Linux (which relies on an ICO
library outside PostgreSQL) they may not all be available. If
necessary you can inspect the list of available collations using the
SQL script;
SELECT collname
FROM pg_collation
WHERE collprovider = 'i'
ORDER BY collname;
This won't actually match the collation the
CREATE
TABLE will use - only the first part needs to match. So if
you see
en-US-x-icu then this means the
en-US collation is available.
So, knowing all the above, what do you need to do?
The answer is, almost nothing.
If you are not creating the database, or the tables, then your program
will behave according to the database collations as they exist.
There's not a lot you can change here. You can retrieve the default
collation of the database using prop:CollationDB.
collation = Customers{prop:CollationDB}
If you are not creating the database, but you are creating the tables,
then it is worth inspecting the default collation for the database to
see if it is consistent with your needs. If not you will likely need
to set the collation for all the strings at the field level, in the
dictionary.
If you are creating the database then pay particular attention to the
collation you use. If you are using the
CREATEDB
command, then the
prop:CollationDB will
be used. The default value, if this is not set by the program, is
en-US-u-ks-level2. This is a case-insensitive
collation, using a pretty standard alphabetical order. If you wish to
change it you can do something like;
Customers{prop:CollationDB} =
'de-DE-u-ks-level2'
CreateDB(Customers,'MyAmazingDatabase')
Naturally this code needs to run before the database is created, once
the database has been created this command no longer does anything.
Case Sensitive / Insensitive Keys
This section is provided for background information. It is unlikely
that you will need to do anything in either the program, or database.
Things should "just work". However if they do not this section is
designed to explain the logic which determines how case sensitivity
works in PostgreSQL.
In Clarion, a case sensitive key has an effect on two parts of the SQL
SELECT statement; the WHERE clause and the ORDER BY clause.
The WHERE clause (ie the prop:Filter) is a equality
The order of characters is determined by the
collation.
In other words, something we call
alphabetical order, is
really just an arbitrary order, and the collation determines what that
actual order is.
There are two collation settings in play for any string in the
database. The first is the default collation of the database, the
second is the collation for the string itself. The default collation
is set when the database is created. If you are using an existing
database, then you can't change this value. You can determine the
collation of the database by executing the SQL
SELECT datcollate FROM pg_database WHERE datname =
'whateverdatabasename';
From your Clarion program you can also query
prop:CollatationDB. This will return the same
value.
collation = Customers{prop:CollationDB}
If the server is running on a Windows computer the value is likely to
be something like
English_United States.1252.
If the server is running on a Linux computer the value is likely to be
en_US.UTF-8. Or (in both cases) it might be
something different like
en-US-u-ks-level2.
These collations (and the other locale-based locations) are case
insensitive. It is very unlikely the database will have a default
case-sensitive collation. This means that by default, sorting of the
strings, and comparison of strings is case insensitive. Which is what
most programs want.
If the collation is set to
C or
Posix
then that would be case sensitive. If the collation is
*-level3
then it is case sensitive. For more on the meanings of collation names
see
collation.
Note that in PostgreSQL, in many collations, lower case comes before
uppercase. So
apple comes before
äpple
comes before
Apple comes before
APPLE comes before
banana.
The order is consistent, but it may be unexpected to see lower case
before upper case in a browse.
In Clarion this all matches a case-insensitive key exactly. Sorting
alphabetically just works, filters just work, and so on.
In Clarion we also have case-insensitive keys. These are keys which
use the underlying ASCII value for sorting.
A
comes before
B, comes before
a,
comes before
b, comes before
ä.This
is not terribly useful for sorting strings (like in browses), but
occasionally it is useful when dealing with case sensitive real-world
identifiers. When creating case-insensitive keys (with strings) in
your dictionary, and when using the key in a
GET
or
SET statement, the matching
level-3
collation (or, if not a ICU collation then the
C
collation) is used.
It is also possible to set the collation of a string field when the
table is created. The desired collation can be set in the dictionary,
using the
Extended
Name Attributes. For example;
Name
String(100),Name('LastName | Collate(en-US-u-ks-level3)')
This would set this column to use the
en-US
collation,
level 3.
Level 3 is case sensitive. In this approach it's not necessary to make
the key case sensitive. The field collation would ensure that the
field is used in a case-sensitive way.
Note that it is possible to create two different keys on the same
column, one case sensitive and the other not. The
GET
or
SET command would then use the
appropriate key for the desired outcome.
See also
prop:Collation,
prop:CollationDB
CREATEDB
The PostgreSQL2 driver creates the database using an
icu
collation. You can set the
collation as described
above.
Driver Options
| Option |
Default Value |
Description |
|
|
|
| /BUSYTIMEOUT=n |
5 |
Sets the lock_timeout for the
connection. Value is in seconds. |
|
|
|
|
|
|
|
|
|
| /WHERE |
|
Used with SEND. Same as setting
{prop:Where}. |
libpq
The Postgres2 driver connects to the Postgres database using the
libpq.dll file. This bypasses the ODBC layer. This is similar to the
sqlite3.dll which the SQlite2 driver uses. The Postgres2 driver ships
with the libpq.dll file (as well as the other DLL's it requires.)
License
libpq is licensed under the
LGPL 2.1 License. The source code to libpq is
available from the
https://www.postgresql.org/ftp/odbc/releases/.
libpq makes use of OpenSSL. Hence this product includes software
developed by the
OpenSSL
Project for use in the OpenSSL Toolkit. This code is Copyright
(c) 1998-2025 The OpenSSL Project. All rights reserved. It is licensed
under the terms of the
Apache license.
Updating libpq
The necessary libpq files ship with the SQL driver kit. However it may
be desirable to update the libpq DLL from time to time so this section
explains where the files come from. Obviously later versions of libpq
may, or may not, be compatible with the driver itself.
Since Clarion (up to at least Clarion 12) is a 32 bit program, it is
necessary to make use of the 32 bit version of libpq. The easiest way
to get this is to install the latest version of the Postgres 32 bit
ODBC driver. This can be retrieved from
https://www.postgresql.org/ftp/odbc/releases/ .
Note that the ODBC (libpq) version number is distinct from the
Postgres database version number. There is a very high degree of
compatibility between all versions of libpq, and Postgres since
PosgreSQL database version 10.
After installing the ODBC driver the location for the actual ODBC
files can be located in the Windows Registry. The key to example in
the registry is
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\ODBC\ODBCINST.INI\PostgreSQL.
Typically they will install into
C:\Program
Files (x86)\psqlODBC. There is likely one or more numbered
sub directories, one of which will contain a
bin
folder. The DLLs will be located in that folder. Note that the number
of the folder may not correspond to the number of the ODBC driver
being installed.
Additional supporting DLL files;
- libssl-3.dll
- libcrypto-3.dll
Name Limits
All names in PostgreSQL are limited to 63 characters. Since index
names are constructed using
tablename_keyname
syntax, it is recommended that table and keynames are limited to
around 30 characters in the dictionary.
Primary Keys
PostgreSQL does not support case-sensitive Primary Keys, and Primary
Keys may not Exclude Nulls. If those options are set for the primary
key, they are ignored.
Storage
In Postgres all
CHAR and
VARCHAR
fields are stored as utf-8 encoded strings.
If your application only makes use of ASCII text (in other words
characters <= 127) then this is not a problem since utf-8 and ASCII
overlap in this range. So an ASCII string is implicitly a utf-8 string
as well. If you are only dealing with English letters, then this is no
issue at all.
The PostgreSQL2 driver automatically converts all
STRING,
CSTRING ,
PSTRING
and
MEMO (not binary) fields into utf-8
encoding before writing, and decodes them after reading. There is
nothing for you to do.
If there is an error when encoding or decoding the data, then an
Error 140 Unable to convert to, or from, unicode
is generated.
When converting from ANSI to utf-8 a CodePage is used. The CodePage
for the file is automatically set (on each thread) based on the
Clarion
system{PROP:CharSet}[1]
setting.For example if your program has set;
System{prop:Charset} = CHARSET:GREEK
Then the code page used will be
Windows 1253.
A list of Clarion Charsets can be found in
Equates.Clw.
If you have not set a charset in your program then the charset used
will be
Windows 1252.
If the automatic code page is not correct for your situation, then you
can set
tablename{prop:codepage} = whatever
In this case you are not setting the charset, but the code page. This
is a value defined by Windows - for example
CP_ISO_8859_1
equate(28591)
The
StringTheory.Inc file contains a
number of code page equates, but any Windows Code Page value can be
used here.
Note 1:
In Clarion 11 a new system property,
System{prop:codePage}
was introduced. If set, this code page is used. If not set then the
System{prop:CharSet} is used as described
above. StringTheory 3.82 or later is required for
System{prop:codePage}
to be used.
SUSPENDRELATIONS/
RESUMERELATIONS
To suspend relationships in PostgreSQL the driver drops all the
foreign key constraints for the table. Thus in PosgreSQL the driver
executes this command on the specific table, not the whole database.
SUSPENDRELATIONS first queries the
database to get a list of existing relationships. This is independent
of calls to
ADDRELATION.
This is stored internally by the driver, and used during to
RESUMERELATIONS
recreate the relationships.
Warning: The suspension works at the
database level, it affects all programs accessing the database. This
command is considered dangerous and should be used with extreme
caution.
NOTE: Calling
SUSPENDRELATIONS
removes the relationships from the database. Failure to call
RESUMERELATIONS
will result in the relationships being permanently removed.
See also
SUSPENDRELATIONS and
RESUMERELATIONS.
Transactions
A PostgreSQL transaction will effectively terminate on the first error
that occurs. It is extremely sensitive in this regard.
So, for example, if an
ADD fails (because
of a duplicate key error, or constraint or whatever then the
transaction has "failed" and
ROLLBACK
will need to be called.