An ADBC driver for querying Power BI semantic models with DAX via the Execute DAX Queries REST API, which returns results natively as Apache Arrow IPC streams. Because the wire format is already Arrow, the driver is a thin streaming adapter: record batches flow from the Power BI service to your DataFrame with no re-serialization.
Built on
driverbase-go.
⚠️ Preview — not for production. This driver targets Fabric's new native Arrow IPC Execute DAX Queries REST API, which is still in preview. That upstream API can change or break without notice, so don't depend on this in production yet.
- Semantic model on a Premium, Fabric, or Embedded capacity (not Pro-only)
- Tenant settings enabled: Dataset Execute Queries REST API and Allow XMLA endpoints; Allow service principals to use Power BI APIs if using a service principal
- Caller has Read + Build permission on the semantic model
- Rate limit: 120 query requests/minute/user — the driver retries 429s
with exponential backoff + jitter (respecting
Retry-After)
Queries. ExecuteQuery runs DAX — SetSqlQuery carries the query text
verbatim (the same way the BigQuery driver carries GoogleSQL) — and returns
a streaming Arrow RecordReader. ExecuteSchema returns the result schema
without materializing rows (via the endpoint's schemaOnly). Prepare is
accepted as a no-op (tools often call it before executing).
Metadata discovery. GetObjects (catalog → schema → table → column, with
depth control and LIKE-pattern filters), GetTableSchema, GetTableTypes
(TABLE, HIDDEN TABLE), and GetInfo. These are built on
INFO.VIEW.TABLES/COLUMNS/MEASURES, so an agent can explore a model before
writing DAX. Catalog = the dataset (semantic model) GUID; schema = model.
Result handling. Fully streaming (memory bounded by one record batch),
transparent LZ4 decompression, dictionary-encoding passthrough or decode,
error rowsets (IsError in schema metadata) surfaced as adbc.Error with
the service FaultCode/FaultString, multi_result modes (first,
error), and automatic 429 retry with backoff.
Authentication. default, azure_cli*, device_code,
interactive_browser, client_secret, client_certificate,
static_token; plus impersonated_user for RLS. (*azure_cli cannot
reach the Arrow endpoint — see Status.)
Not supported — by design, because the Execute DAX Queries endpoint is a read-only query API:
- Writes / bulk ingest (
ExecuteUpdatereturnsNotImplemented) - Bound query parameters (
Bind) - Transactions (autocommit only)
- Substrait (
InfoVendorSubstrait = false) ExecutePartitions(reserved;multi_result=partitionserrors today)- DAX
Variantcolumns (dense_union): an optional cast to utf8 is not yet implemented
powerbi-adbc/
├── go/ # the Go module (github.com/crazy-treyn/powerbi-adbc-driver)
│ ├── powerbi.go # NewDriver, option constants, DriverInfo
│ ├── database.go # option validation, credential construction, Open
│ ├── connection.go # statements, catalog/schema surface, error mapping
│ ├── metadata.go # GetObjects via INFO.VIEW.{TABLES,COLUMNS,MEASURES}
│ ├── statement.go # DAX passthrough, request params, ExecuteQuery/Schema
│ ├── driver_test.go # end-to-end suite against a mock service
│ ├── internal/
│ │ ├── auth/auth.go # Entra ID credential chain (azidentity)
│ │ └── client/
│ │ ├── client.go # POST executeDaxQueries, retry, error mapping
│ │ ├── stream.go # concatenated-IPC splitter, error-rowset
│ │ │ # detection, dictionary decoding
│ │ └── *_test.go
│ ├── manifest.toml # driver metadata (name/publisher/license/[ADBC])
│ ├── pkg/ # generated C ABI wrapper — see Building
│ └── validation/ # live-tenant pytest suite (skips without creds)
├── examples/powerbi.toml # ADBC driver manifest for local install
├── examples/profile-mymodel.toml# ADBC connection profile example
└── examples/mock_smoke_test.py # verify the built shared lib via Python + mock service
Conventions: Apache-2.0 headers on every file (rat-check compatible),
driverbase-go for all ADBC boilerplate, manifest.toml metadata, options
under the powerbi.* namespace, errors raised through
driverbase.ErrorHelper with proper adbc.Status codes, and a C-ABI pkg/
generated with driverbase-go's ffitemplate rather than written by hand.
| Option | Notes |
|---|---|
powerbi.dataset_id |
Required. Semantic model GUID |
powerbi.workspace_id |
Workspace GUID; omit for My Workspace |
powerbi.auth_type |
default | azure_cli | device_code | interactive_browser | client_secret | client_certificate | static_token. Note: azure_cli cannot reach the Arrow executeDaxQueries endpoint (see Status). Use client_secret (service principal) or device_code/interactive_browser with an app registration granted delegated Dataset.Read.All. |
powerbi.tenant_id / client_id / client_secret / client_certificate_path |
Per flow |
powerbi.access_token |
Pre-acquired bearer token (static_token) |
powerbi.endpoint |
Default https://api.powerbi.com; override for sovereign clouds |
powerbi.impersonated_user |
effectiveUserName for RLS (not supported with service principals upstream) |
powerbi.strings.dictionary_encoded |
true (default) passes dictionary-encoded utf8 through; false decodes |
Statement options: powerbi.statement.query_timeout_seconds,
.resultset_rowcount_limit, .memory_limit_mb, .multi_result
(first|error|partitions). .execution_metrics is reserved and
currently rejected when enabled until metrics rowsets are validated against
the live service.
- The "SQL" dialect is DAX.
SetSqlQuerycarries DAX text verbatim, the same way the BigQuery driver carries GoogleSQL.InfoVendorSqlis reportedfalse. - Errors hide inside HTTP 200. The service returns query failures as
an error rowset (
IsError=truein Arrow schema metadata) possibly after data rowsets in the same body. The stream reader checks metadata on every embedded IPC stream and surfaces failures asadbc.Errorwith the serviceFaultCode/FaultString. - One body, many IPC streams. One self-contained stream per
EVALUATE. Default mode surfaces the first data rowset and scans trailing rowsets for errors when the first rowset is fully consumed; earlyRelease()closes the response without draining.multi_result=errorrejects additional data rowsets;partitionsis reserved forExecutePartitions. - Fully streaming. Batches are surfaced as they arrive over chunked transfer; memory use is bounded by one record batch. LZ4 buffer compression is decoded by arrow-go's IPC reader transparently.
- Metadata via INFO functions. DMVs aren't supported by the endpoint,
but
INFO.VIEW.TABLES()/INFO.VIEW.COLUMNS()/INFO.VIEW.MEASURES()are —GetObjectsis built on them, which is what lets an AI agent discover the model before writing DAX.GetTableSchemausesschemaOnly=truewithEVALUATE TOPN(0, 'Table').
The shared library's file extension is a per-platform convention you choose
via -o (Go builds the same c-shared artifact either way):
| Platform | Build output (use with -o) |
|---|---|
| Linux | libadbc_driver_powerbi.so |
| macOS | libadbc_driver_powerbi.dylib |
| Windows | adbc_driver_powerbi.dll |
Substitute your platform's name for <lib> below.
cd go
go build ./...
go test ./...
# Build the shared library (the generated C ABI wrapper in pkg/ is checked in).
# Linux example — swap the -o name per the table above:
go build -tags driverlib -buildmode=c-shared -o <lib> ./pkg
# Verify the shared library end to end (no tenant needed):
pip install adbc-driver-manager pyarrow
python ../examples/mock_smoke_test.py <lib>Note: go mod tidy currently fails on a broken test-only dependency
declared by upstream driverbase-go (testutil pseudo-version); use
go get ./... / go get -t ./... to manage dependencies until that is
fixed upstream.
To regenerate pkg/ after a driverbase-go upgrade, run driverbase-go's
ffitemplate from a checkout (it needs goimports and clang-format on
PATH; the tool cannot be go run by module path today because its
declared module path diverged from its directory):
git clone https://github.com/adbc-drivers/driverbase-go /tmp/driverbase-go
(cd /tmp/driverbase-go/ffitemplate && go run . -prefix PowerBI -driver /path/to/this/repo/go)Install locally with an ADBC driver manifest so every driver manager finds
it by name (see examples/powerbi.toml; drop it in
~/.config/adbc/drivers/ on Linux,
~/Library/Application Support/ADBC/drivers/ on macOS, or a directory on
ADBC_DRIVER_PATH).
Python (what most agent tools shell into):
import adbc_driver_manager.dbapi as adbc
with adbc.connect(
driver="powerbi",
db_kwargs={
"powerbi.workspace_id": "WORKSPACE_GUID",
"powerbi.dataset_id": "DATASET_GUID",
# Service principal added to the workspace (azure_cli does NOT work
# against the Arrow endpoint — see Status).
"powerbi.auth_type": "client_secret",
"powerbi.tenant_id": "TENANT_GUID",
"powerbi.client_id": "APP_CLIENT_ID",
"powerbi.client_secret": "APP_CLIENT_SECRET",
},
) as con, con.cursor() as cur:
cur.execute("EVALUATE TOPN(100, 'Sales')")
print(cur.fetch_arrow_table().to_pandas())Or keep credentials out of code with a connection profile
(examples/profile-mymodel.toml), then from DuckDB:
INSTALL adbc FROM 'https://columnar-tech.github.io/duckdb-adbc-client';
LOAD adbc;
SELECT * FROM read_adbc('profile://mymodel', 'EVALUATE INFO.VIEW.MEASURES()');databow is a small CLI for querying any ADBC driver. Build the shared library and point a connection profile at it — no manifest required.
Prerequisites: Go (to build), uv, and a
Power BI service principal with Read+Build on a model hosted on
Premium/Fabric capacity.
# 1. Build the driver. Name the -o output per your platform (see Building):
# Linux .so · macOS .dylib · Windows .dll
cd go && go build -tags driverlib -buildmode=c-shared \
-o libadbc_driver_powerbi.dylib ./pkg # macOS example
# 2. Install the CLI
uv tool install databow-
Create one file,
powerbi.toml— the profile is the only config you need. Itsdriverkey can be a driver name (needs an installed manifest) or, as here, a direct path to the shared library you built in step 1:profile_version = 1 # point at the .so / .dylib / .dll you built above driver = "/absolute/path/to/go/libadbc_driver_powerbi.dylib" [Options] "powerbi.auth_type" = "client_secret" "powerbi.workspace_id" = "WORKSPACE_GUID" "powerbi.dataset_id" = "DATASET_GUID" "powerbi.tenant_id" = "TENANT_GUID" "powerbi.client_id" = "APP_CLIENT_ID" "powerbi.client_secret" = "APP_CLIENT_SECRET"
-
Query (the query string is DAX, always starting with
EVALUATE):databow --profile powerbi.toml --query "EVALUATE INFO.VIEW.TABLES()" databow --profile powerbi.toml --query "EVALUATE TOPN(10, 'YourTable')" databow --profile powerbi.toml --file report.dax # from a file databow --profile powerbi.toml --query "EVALUATE 'Sales'" --output out.csv databow --profile powerbi.toml # interactive shell
A driver manifest (examples/powerbi.toml in
~/Library/Application Support/ADBC/drivers/ on macOS) is only needed if you
prefer to reference the driver by name — databow --driver powerbi or
driver = "powerbi" in the profile — instead of by path.
v0.1. The stream splitter, options surface, auth chain, statement
lifecycle, metadata enumeration (GetObjects via INFO.VIEW.*, measures
included), and dictionary decoding are all implemented, with unit tests plus
an end-to-end suite (Go and Python) against a mock service. Validated against
a live Fabric (F64) tenant (2026-07-11): metadata discovery, DAX queries,
multi-batch streaming (500k rows), dictionary encoding, schema-only, and the
error-rowset path all confirmed — see go/validation/. ExecutePartitions
(multi_result=partitions) is reserved for v0.2.
Auth caveat. auth_type=azure_cli does not work against the
executeDaxQueries (Arrow) endpoint — the Azure CLI can only mint a
user_impersonation token, which the endpoint rejects (HTTP 401), and Azure
AD refuses to grant the CLI app the Dataset.Read.All scope. Use a service
principal (client_secret) or an app registration with delegated
Dataset.Read.All. See Connection options.
Apache-2.0. Not affiliated with Microsoft.