Color scheme: Dark
You are here: start » en » Documentation » Data integration and data access » WKL OData ODBC Driver » OData ODBC driver reference » Requirements for an OData service (reference)

Requirements for an OData service (reference)

The driver talks to any OData v4 service that offers the required features below. If a service also offers one of the extensions, the driver uses it; without it, the driver behaves as if it didn't exist. You need this page if you check whether a service works with the driver, or if you build a service for Access applications yourself. The data service Wkl.Data.Gateway offers all features on this page.

Syntax

GET  <service-root>/$metadata                       schema document (CSDL, XML)
GET  <service-root>/<entity-set>?$select=…&$filter=…     reading, JSON response
POST/PATCH/DELETE <service-root>/<entity-set>[(<key>)]   writing

Parameters

The features are grouped by how binding they are for the driver: without the requirements for reading, the driver doesn't connect or reads incorrectly; without the requirements for writing, all sets remain read-only; the extensions are recommendations whose absence turns off a feature but falsifies nothing. The fundamentals of OData v4 are described in the OASIS standards: OData Version 4.01 Part 1: Protocol, Part 2: URL Conventions (query options, literals), OData Common Schema Definition Language (CSDL) XML Representation 4.01 ($metadata), and OData JSON Format 4.01. The driver requires only features that OData 4.0 already has; it uses $query (4.01) if available.

Required for reading

FeatureWhat the driver needs it for
$metadata (CSDL) with entity sets, properties, types, facets, and keyscatalog functions, type mapping (see Escape clauses and data types)
Only the entity sets released for the caller in $metadataAccess offers what the user may also read. That's a requirement for the service's security, not for the technology: the driver connects without it too, but then shows tables that fail to read
$select, $filter, $orderby, $top, $skiptranslation of SELECT
In $filter, the OData v4 literals without type names, also for Edm.Guid (Id eq 3333…), and comparisons of two properties (ParentId eq Id)conditions on uniqueidentifier columns; roots of a tree. A service without field-to-field comparison answers 400, the driver reports 42000 – only this one condition fails
$countCOUNT(*) without loading the rows
@odata.nextLinkreading large sets page by page
Prefer: odata.maxpagesize (recommended)larger pages when only narrow columns are read, such as the keys. A service that ignores the preference returns its own page size; the driver then reads more pages, and nothing else changes
Responses as JSONthe driver doesn't read Atom/XML

Required for writing

FeatureWhat the driver needs it for
Key in the entity type and addressing Set(Key)PATCH, DELETE (reading uses $filter, never the key address); a set without a key is read-only (except with the actions for tables without a key, below)
POST, PATCH, DELETE with a JSON body; response with the stored recordadding, changing, deleting
If-Match, response 412 for an outdated version, announcement with Core.OptimisticConcurrencydetecting write conflicts; without this announcement, the driver rejects changing and deleting (adding remains possible). The driver builds the ETag itself from the announced column (binary value as W/"<Base64>"); the service must accept this form, see What the driver assumes when writing

Extensions (recommended) that the driver uses if the service offers them

FeatureHow the driver recognizes itWhat for
Address for custom statements: accepts {"sql": …, "parameters": {…}}, answers {"columns": […], "data": […], "affectedRowCount": …}the connection names it (PASSTHROUGH), or $metadata names it in Store.PassThroughstatements that can't be translated, pass-through queries
Org.OData.Aggregation.V1.ApplySupported with the transformations filter, compute, groupby, aggregate; Aggregation.CustomAggregationMethods with Wkl.Store.V1.countannouncement in $metadatatotals, groups, HAVING, DISTINCT without custom statements; COUNT(Spalte)
Bound actions UpdateByOriginal and DeleteByOriginal on a set without a key$metadata declares both for the type of the setchanging and deleting by the old values
POST <resource>/$query with the query options in the body (OData 4.01)tried as soon as an address would be longer than MAXURLLENGTH; without this form, the service answers 404, 405, or 501, and the request goes as GET after alllong conditions
Sign-in through HTTP Negotiatethe connection turns it on (AUTH=WINDOWS); the service requests it with 401 and WWW-Authenticate: NegotiateWindows sign-in without a secret
Sign-in through HTTP Basicthe connection names UID and PWDsign-in with name and password
Key in a headerAPIKEY/APIKEYENV or AUTH=KEY; header APIKEYHEADER, default X-Api-Keyservice keys and personal keys
Second factor: 401 with WWW-Authenticate: Wkl-Totp … endpoint="<path>"; POST <path> with {"code": "…"} returns 201 with a sessionchallenge in the responsecode prompt, session in the X-Wkl-Session header
Identity choice: header X-Wkl-Role; rejection 403 with X-Wkl-Roles (percent-encoded UTF-8, separated by commas)header in the responsechoice of the database identity
Idempotency-Key on POST, PATCH, DELETE: a retry receives the stored first response– (the driver always sends the key)retry after a dropped connection without a duplicate record
Vocabulary Wkl.Store.V1 (terms below)annotations in $metadataappearing as a database product, schema as TABLE_SCHEM, type names, indexes, statistics for Access's query planning
Transactions: Store.Transactions on the entity container names an address; POST to it returns 201 with {"id": "…"}; requests with the header Wkl-Transaction: <id> run in it; POST <address>/<id>/commit or /rollback closes it (204); an unknown or ended ID 404, one rolled back by the service 409annotation in $metadataBeginTrans/CommitTrans/Rollback, BeginTransaction; SQL_TXN_CAPABLE = SQL_TC_ALL
Database messages in error.details of a rejection: per entry code = message number as text, message, plus sqlState (five characters)entries with a number as codeone diagnostic record per message with the number as the native error number and the stated SQLSTATE (if missing: 42000), as with the SQL Server driver; without such entries, one record with the HTTP status as the number
Core.Computedannotation on the propertycolumns that can't be written (identity, computed, row version); the driver reports them as read-only through SQLColAttribute (SQL_DESC_UPDATABLE), all others as “unknown”

Terms of the Wkl.Store.V1 vocabulary. The vocabulary is an extension of this product, not part of OData. A service that offers it places it under its own address and includes it with edmx:Reference (example below); the driver recognizes the terms by their namespace, with or without an alias.

TermTypeApplies toMeaning for the driver
ProductEdm.Stringentity containername of the database product, reported through SQLGetInfo(SQL_DBMS_NAME); without the term, the driver reports OData and version 4.0
ProductVersionEdm.Stringentity containermajor and minor version (17.00), reported as SQL_DBMS_VER in the form 17.00.0000
SchemaEdm.Stringentity containerschema of the service root, reported as TABLE_SCHEM; without it, Access stores an empty source name for a table linked as dbo.Tabelle
PassThroughEdm.Stringentity containeraddress for custom statements; adopted only at the same origin as the service root
TransactionsEdm.Stringentity containeraddress that opens a transaction
TypeEdm.Stringpropertydatabase type name, for a self-numbering column with identity
Indexeslist of Index (Name, Unique, Primary, Clustered, Columns)entity typeindexes for SQLStatistics
Rows, PagesEdm.Int64entity typerows and data pages for SQLStatistics (CARDINALITY, PAGES); estimates

Commitments per kind of service

Which of the driver's commitments applies depends on what the service offers and honors. The driver recognizes announcements in $metadata, but not whether a service actually honors a header such as Idempotency-Key. What it can't recognize, you must clarify with the service's provider or verify yourself before use.

Commitmentany OData v4 service with the required featuresservice with the extensions on this pagedata service Wkl.Data.Gateway
Reading, filtering, sorting, counting on the serviceyesyesyes
Writing with write conflict detectionyes, with Core.OptimisticConcurrencyyesyes
Retry of a read after a dropped connectionyesyesyes
Retry of a change without a duplicate recordno: the driver retries by default anyway; a service that ignores Idempotency-Key can create a record twice. Remedy: RETRY=OFFonly if the service honors the header – not detectable, clarify with the provideryes: the service keeps the response in memory for ten minutes
Transactionsno (HYC00; no effect in DAO)with Store.Transactionsyes
Totals and groups without pass-throughnowith Aggregation.ApplySupportedyes
Statements that can't be translatedno, 42000with a pass-through addressyes: checked statements for every caller whose rule names the alias; unchecked SQL only when enabled
Second factor, identity choicenowith the challenges on this pageyes

What a service must commit to for retries. The driver retries a change at most twice, after 1 and after 3 seconds, with the same Idempotency-Key. The service must therefore keep the first response for a key for at least a few seconds and return exactly that response to the retry, without executing the change again. The driver doesn't retry an exceeded time limit (HYT00): whether the change was executed is then unknown, and the application must read the record again. It doesn't retry the completion of a transaction (commit, rollback) either.

What a service must commit to for security reasons. It refuses a request without a valid sign-in or second factor before it executes anything; the driver then sends it again. And it doesn't redirect addresses under the service root to another origin (see Residual risks).

Return value

None. The driver checks the features when connecting and at the time of each use, not in advance.

Remarks

  • Without Core.OptimisticConcurrency, Access puts the old values of all columns into the condition when changing. That can't be expressed in OData; the driver rejects it instead of dropping the check. The remedy lies with the service, for example with a computed concurrency property.
  • Without grouping and without an address for custom statements, totals queries and joins from Access fail with “ODBC–call failed”; see Troubleshooting.
  • Without Idempotency-Key support, a retry after a dropped connection can create a record twice or report a write conflict even though the change went through. A service that doesn't know the header ignores it; the driver doesn't detect that. The header is described by an expired IETF draft, not a standard (see Standards and specifications).
  • Custom statements on the wire. The driver sends {"sql": …, "parameters": {…}} and expects {"columns": […], "data": […], "affectedRowCount": …}. Bound values remain values: the ? placeholders, which ODBC counts by position, become named placeholders – a question mark in a string, a bracketed name, or a comment remains untouched. A response without columns is rejected. Inferring the types from the values of the first row would mean: an integer in a decimal column becomes an integer, a timestamp becomes text, and an empty result has no shape at all – while the application binds its buffers before it reads the first row.
  • Output parameters, further result sets, describing. When an application binds output parameters, the driver also sends "outputs": {"p1": "Edm.Int32", "p2": {"type": "Edm.String", "maxLength": 20}} and reads the values from outputs in the response; it reads further result sets from results (each with columns, data, truncated). When an application asks for the columns before execution, the driver sends "describe": true and expects the columns of the first set without the service running anything. It asks for a description only from a service that names the address in Store.PassThrough itself: a service that ignores describe would otherwise run the statement. If a service returns no outputs, the application gets NULL as the output value.
  • Description of the columns. Besides name, type, and the facets (maxLength, precision, scale), the driver reads three optional members per column: storeType (the database's type name, like Store.Type in $metadata), identity (true for a value the database assigns), and label (the name the database gives the column, where it differs from name). Rows are JSON objects, so for a column without a name or with a name already taken, the service has to assign a key of its own. With label, the driver shows the application the database's name – empty or repeated, as ODBC Driver 18 does – and reads the values under name. Without label, the application sees that key.
  • Store.PassThrough applies only at the same origin (scheme, host, port) as the service root.
  • The driver skips fields of a response that it doesn't know. On the pass-through route, it reads columns, data, affectedRowCount, truncated, results, and outputs (see Limits).
  • No redirect to another origin. The driver follows a redirect only when reading and only to the same scheme, host, and port; if the service redirects elsewhere, the request fails with 08S01.

Example

Excerpt of a schema document with the extensions that Access needs for editing and grouping:

<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Aggregation.V1.xml">
  <edmx:Include Namespace="Org.OData.Aggregation.V1" Alias="Aggregation" />
</edmx:Reference>
<edmx:Reference Uri="https://daten.example.com/odata/v4/$vocabulary/Wkl.Store.V1.xml">
  <edmx:Include Namespace="Wkl.Store.V1" Alias="Store" />
</edmx:Reference>
…
<EntityContainer Name="Container">
  <Annotation Term="Store.Product" String="Microsoft SQL Server" />
  <Annotation Term="Store.ProductVersion" String="17.00" />
  <Annotation Term="Store.Schema" String="dbo" />
  <Annotation Term="Aggregation.ApplySupported">
    <Record>
      <PropertyValue Property="Transformations">
        <Collection><String>filter</String><String>compute</String><String>groupby</String><String>aggregate</String></Collection>
      </PropertyValue>
      <PropertyValue Property="Rollup" EnumMember="Aggregation.RollupType/None" />
    </Record>
  </Annotation>
  <Annotation Term="Aggregation.CustomAggregationMethods">
    <Collection><String>Wkl.Store.V1.count</String></Collection>
  </Annotation>
</EntityContainer>

This is what the data service returns (abridged). The terms appear with the alias that the edmx:Reference assigns; the driver reads both notations.

Applies to

Driver Wkl OData, both bitnesses. OData v2 and v3, as well as services without $metadata or without JSON, aren't supported.

Exceptions

SQLSTATECondition
42000A statement can't be translated, and there's no address for custom statements
HYC00The application requests a transaction, and the service doesn't offer any (no Store.Transactions)
40001SQLEndTran with commit: the service has already rolled back the transaction (after an error that made it unusable, or after its idle time); none of it is saved
08S01The service isn't reachable, or a document breaks off (for example a streamed response)

See also

en/wiki/data-conectivity/odata-odbc/reference/requirements-for-the-service.txt · Last modified: by Wolfgang van der Stille