aster.Web Data / Docs Aster UI ↗

Aster Web Data / Connect your data

SQL ServerCommercial

How Aster Web Data uses SQL Server, and how SQL Server's types, collations and results shape what clients receive.

If your data is in SQL Server 2019 or later, Aster Web Data serves it from your existing tables, with filters, sorts, pages and counts written as T-SQL that SQL Server's planner can serve from your indexes. Each comparison uses the column's own collation, and each result is SQL Server's own. Where it differs from OData's, SQL Server's answer stands; Departures from OData lists those places.

Three choices change what your users see: the collation of your text columns, which decides case and accent sensitivity; whether strings in keys and indexes have a declared maximum length, which the start-up check requires; and the rowversion column that gives a set its ETag.

Connecting

Declare the database once under databases:, and name it from each data source stored on it:

databases:
  orders:
    kind: mssql
    url_file: secrets/orders.url
    tls: verify-full
    ca_file: secrets/sql-ca.pem    # optional

The connection comes from an environment variable (url_env), a file (url_file), or its parts (host, port, user, password_env or password_file, and database). SQL Server authentication is supported. Secrets explains the rules, and the configuration reference lists every key with its default. These behave in a way particular to SQL Server:

The service checks the server when it connects: SQL Server 2019 or later, with a database compatibility level of 150 or higher. It refuses an older one, naming the version and level it found. The tests run on SQL Server 2022.

Start-up check

When the service opens the database, it reads the sys catalog for each data source's table and checks it against the configuration: the columns, their types and facets, identity and computed columns, defaults, keys and foreign keys. The account sees only what its permissions allow.

Two rules are particular to SQL Server. A string used in a key or an index needs a declared maxLength, because SQL Server limits an index key to 900 bytes for a clustered primary key and 1,700 bytes for a nonclustered index; the check names the field that lacks one. And an existing text column must be NVARCHAR: a VARCHAR column is refused, because binding an NVARCHAR value against it can convert the indexed column and lose the index.

A failed check makes only that database's sets unavailable; A database goes down describes what clients see. To run the check on its own, use the check command.

Types

OData type Column created by recreate Notes
Edm.Boolean bit
Edm.Int64, enumerations bigint Narrower integers get a CHECK on their range.
Edm.Decimal(p, s) decimal(p, s) Both facets are required, with p from 1 to 38. A value too large for a 28-digit decimal is refused when read.
Edm.Single, Edm.Double float(53) Finite values, signed zero and subnormals. NaN and INF are refused.
Edm.String nvarchar(2 × maxLength), otherwise nvarchar(max) Two UTF-16 units are reserved per character. The empty string and NUL are kept.
Edm.Binary varbinary(max)
Edm.Guid uniqueidentifier
Edm.Date date Years 1 to 9999.
Edm.TimeOfDay time(7) 100 ns. Finer fractions and leap seconds are refused.
Edm.DateTimeOffset datetimeoffset(7) 100 ns, written and read in UTC.
Edm.Duration bigint Signed nanoseconds.
128-bit unsigned integer varchar(39) COLLATE Latin1_General_100_BIN2 39 zero-padded digits, with a CHECK on the range. Arithmetic on them runs in the service.
JSON, collections of text, integers or booleans nvarchar(max) JSON text.

A decimal is read from the wire's exact value and checked against the 28-digit bound, which 29 digits meet only up to 79,228,162,514,264,337,593,543,950,335; it is never converted through a double. A literal written to a decimal column is checked against the declared facets. A row with an offset written by another client is still returned in UTC, though year, month and the other date parts read the stored offset.

Collation and case

A comparison, a sort, grouping and in use the column's collation, with SQL Server's own operators and no correction. $metadata publishes each text field's collation as AsterData.Collation.

A named collation whose comparison the service cannot reproduce is refused when a request would need it outside SQL Server.

Where each operation runs

Each OData operator becomes the SQL Server operator with the same meaning:

OData SQL Server Notes
eq, ne, lt, le, gt, ge =, <>, <, <=, >, >= ne on a nullable column adds IS NULL.
not, and, or, in NOT, AND, OR, IN
add, sub, mul, div, mod +, -, *, /, % Integer division truncates toward zero.
divby / over operands cast to float
contains, startswith, endswith LIKE ... ESCAPE
indexof, substring CHARINDEX - 1, SUBSTRING(s, start + 1, len) Zero-based, as OData is.
trim, length, concat TRIM, LEN, CONCAT
tolower, toupper LOWER, UPPER
ceiling, floor, round CEILING, FLOOR, ROUND(x, 0)
year, month, day, hour, minute, second DATEPART
count, sum, avg COUNT_BIG, SUM, AVG COUNT_BIG avoids COUNT's 32-bit limit.
$orderby, paging ORDER BY, OFFSET ... FETCH A page with no order gets a constant one, and an empty page uses TOP(0).
$expand key batches One statement per level.

These run in the service, within the limits on rows it may hold: casts that SQL Server cannot do exactly, Traverse, 128-bit arithmetic, and windows over a table with text columns. On a table with a collection column, the text functions run in the service too.

A statement binds at most 2,098 values, which bounds key batches and multi-row writes. A key of several columns is matched with OR over each tuple. A page with $count=true sends the page and the count in one request, inside one serializable transaction, so the total agrees with the page.

Errors

SQL Server error Response
8115, 8134, 245, 8114: overflow, division by zero, conversion 400 EvaluationFailed
2601, 2627, 547, 515: unique, foreign key, not null 409 Conflict
1205, 3960: deadlock, snapshot conflict 409 TransactionConflict, after the atomic group's retries
1222: lock timeout 503 Timeout
18456, 4060, 40197, 40613: login or database unavailable 503 ConnectionLost
Any other database error 500 InternalError. The client gets a generic message, and the database's detail goes to the log.

A timeout or a lost connection discards the connection; an evaluation error or a conflict keeps it.

Nulls and ordering

SQL Server sorts nulls first in ascending order, as OData does. Where a sort needs the other placement, the service writes a CASE key ahead of the sort key. uniqueidentifier values sort in SQL Server's byte order, which differs from the order of their text.

$count=true is exact and agrees with the page.

Departures from OData

Where OData 4.01 defines a result and SQL Server gives another, SQL Server's result stands. These are the places:

Writes and transactions

Write Statements
POST 1
POST with $expand 3
Nested insert of a parent and two children 4
PATCH, PUT or DELETE by key, with or without an ETag 2, in one round trip

Indexes and sort order

Index the columns your screens filter and sort on, as Indexes describes. Equality and a prefix LIKE keep the column bare, so SQL Server can choose a seek. An expression such as LOWER(name) needs an indexed computed column where you want an index to serve it.

Scaffold

The scaffold command reads the sys catalog for schemas, tables and views, columns with their types and facets, identity and computed columns, defaults, comments, keys and foreign keys, and writes a starting data-source configuration. It declares a rowversion column as the set's ETag token. Connect your own database walks through it.