Skip to main content

Fast QVD file parser — Python bindings for qvd-parser (Rust)

Project description

qvd-parser

Crates.io PyPI License: MIT

Fast, correct QVD (QlikView Data) file parser written in Rust.

Reads the binary format used by QlikView and Qlik Sense to store compressed data tables. Exposes a Rust API, a Python API (via PyO3), and a CLI. Export targets: JSON, CSV, Parquet.

Note: QVD is a proprietary file format associated with QlikView and Qlik Sense. This project is not affiliated with or endorsed by Qlik. See Legal & Interoperability for details.

Workspace structure

qvd-parser/
├── core/               # Core parsing library — QvdFile, QvdValue, RowIter
├── convert/            # Export backends: Parquet, JSON, CSV
├── cli/                # Command-line interface (qvd-parser-cli binary)
└── bindings-python/    # Python bindings via PyO3 + maturin

Prerequisites

Build

# Build all crates
cargo build --workspace

# Build release (recommended for performance measurement)
cargo build --release --workspace

# Run all Rust unit tests
cargo test --workspace

# Run integration tests (requires QVD fixtures — see below)
cargo test --workspace --features integration-tests

# Build the Python extension in-place (for development)
maturin develop

# Run Python binding tests
pytest bindings-python/tests/

CLI usage

# Schema inspection
cargo run -p qvd-parser-cli -- schema --input data.qvd

# Export to JSON
cargo run -p qvd-parser-cli -- export --input data.qvd --format json --output out.json

# Export to CSV (semicolon delimiter)
cargo run -p qvd-parser-cli -- export --input data.qvd --format csv --output out.csv --csv-delimiter ";"

# Export to Parquet (verbose timing breakdown)
cargo run --release -p qvd-parser-cli -- export --input data.qvd --format parquet --output out.parquet --verbose

Parquet export options

Flag Default Description
--row-group-size N 100000 Rows per Parquet row group
--encoding auto|plain|dictionary auto Arrow array encoding strategy (see below)
--no-statistics off Disable column min/max statistics (~20% faster export, less efficient predicate pushdown for consumers)
--strict off Fail immediately on cast errors instead of writing null

Verbose timing output

--verbose shows a per-phase breakdown of both parsing and export:

[qvd] export: data.qvd
  load header+symbols+rows        124 ms  [140 fields  119820 rows  126 B/rec]
    header                          1 ms
    symbols                        23 ms
    rows                           98 ms
  export parquet                  555 ms  [out.parquet]
    type inference                  0 ms
    index transpose                43 ms
    cast to arrow                 166 ms
    parquet encode                296 ms
    parquet close                  49 ms

Python usage

import qvd_parser

f = qvd_parser.QvdFile.open("data.qvd")
print(f.table_name, f.row_count, f.field_names())

for row in f.rows():
    print(row)  # list of Python-native values (int, float, str, tuple, None)

# Export
f.to_parquet("out.parquet")
f.to_json("out.json")
f.to_csv("out.csv", delimiter=";")

Python version compatibility

Tested against Python 3.13. CPython 3.11 and 3.12 are declared compatible for Snowflake runtime support but are not covered by CI.

Test fixtures

Integration tests require real QVD files generated by Qlik — synthetic QVD files are not possible due to the proprietary bit-packing format.

Place fixture files in fixtures/ at the workspace root:

File Content
test_data01.qvd 2 columns (id: Int, label: Text), 2 rows: [1, "test1"], [2, "test2"]

Tests that rely on missing fixtures are either skipped or marked #[ignore].

Feature flags

Flag Crate Effect
integration-tests core, convert Enable tests that require fixture files
serde core Derive Serialize/Deserialize on public types

Release workflow

This project uses two separate repositories:

  • GitLab (private) — daily development, full commit history
  • GitHub (public) — published releases only, single commit per version

GitHub receives only validated, committed code from GitLab. The full development history never appears on GitHub.

Publishing a new release (Windows)

Ensure the version to publish is committed and pushed on GitLab main:

# 1. From the GitLab workspace, export the clean content
cd C:\path\to\qvd-parser
git checkout main
git pull origin main
git archive HEAD | tar -x -C C:\path\to\qvd-parser-public

# 2. Commit and push to GitHub
cd C:\path\to\qvd-parser-public
git add .
git commit -m "Release vX.Y.Z"
git push github main

git archive HEAD exports exactly the content of the last commit, respecting .gitignore. Files not committed (build artifacts, fixtures, .venv) are automatically excluded.

GitHub repository setup (once)

mkdir C:\path\to\qvd-parser-public
cd C:\path\to\qvd-parser-public
git init
git remote add github git@github.com:nicolasstefaniuk/qvd-parser.git
git config user.email "287317062+nicolasstefaniuk@users.noreply.github.com"
git config user.name "Nicolas Stefaniuk"

Performance — Parquet export investigation

Context

QVD files use a columnar symbol table + row-major bit-packed index buffer. Parsing is inherently row-major (impossible to read one column without traversing all rows). Parquet is column-major. This impedance mismatch is the core performance challenge.

Measurements (release build, 140 fields × 119 820 rows × 16.5 MB QVD)

load header+symbols+rows    ~120 ms
  header                       1 ms
  symbols                     23 ms
  rows (bit-unpack)           95 ms

export parquet              ~650 ms
  type inference               0 ms
  index transpose             43 ms   ← column_indices_range
  cast to arrow              160 ms   ← QvdValue → Arrow arrays
  parquet encode             400 ms   ← RLE/dictionary encoding (Arrow writer)
  parquet close               48 ms

Ratio parsing:export ≈ 1:5. The bottleneck is the Arrow/Parquet writer, not our code.

Hypotheses tested

1. Cache thrashing on strided index access (fixed)

The original export collected per-column indices by striding through the row-major buffer (n_fields × 4 bytes per step), causing cache misses on wide files. Fixed by adding QvdFile::column_indices_range() in core: a single sequential pass that scatters into n_fields contiguous vectors. Measured gain: ~15% on index transpose.

2. Dictionary encoding for string columns (implemented, marginal gain)

With only 11–15 distinct values per column out of 119 820 rows, passing pre-built DictionaryArray<Int32, Utf8> to the Parquet writer should avoid per-row string copies and skip the encoder's internal deduplication.

Result: ~6% total gain in release, negligible. The Arrow writer (arrow-rs 53.x) still does substantial work on DictionaryArray. Additionally, numeric dictionary arrays (DictionaryArray<Int32, Int64> etc.) are not supported by ArrowWriter in this version and panic at runtime — restricted to Utf8 only.

Available as --encoding dictionary for experimentation. Not recommended for production use until arrow-rs improves dictionary support.

3. Column statistics (implemented, ~20% gain)

Parquet column statistics (min/max per page) cost ~130 ms on this file. They are enabled by default because they allow query engines to skip row groups during reads, which typically saves far more time than the export cost.

Disabling them with --no-statistics saves ~20% export time. Use when export speed is critical and the consumer does not rely on statistics.

Conclusion

The 5× ratio between parsing and Parquet export is inherent to the Arrow writer, not to our code. The practical levers available in our layer are:

Optimisation Gain Status
column_indices_range (sequential scatter) ~15% index transpose Enabled by default
--no-statistics ~20% parquet encode Opt-in flag
--encoding dictionary <10% on Utf8 columns Experimental, opt-in

A more significant improvement would require either a lower-level Parquet crate (e.g. parquet2) or parallelising column encoding — both are substantial changes outside the current scope.


Legal & Interoperability

Trademark notice

"Qlik", "QlikView", and "Qlik Sense" are registered trademarks of Qlik Technologies Inc. The file extension "QVD" is a technical descriptor for the file format, not a registered trademark.

This project is not affiliated with, endorsed by, or sponsored by Qlik in any way. The name qvd-parser is used solely to describe what the library does — reading files in the QVD format — and constitutes nominative descriptive use, not trademark infringement.

Reverse engineering and interoperability

This library was developed by observing the structure of QVD files produced by Qlik software. No Qlik source code was copied, decompiled, or incorporated. The parser is an independent implementation.

Reading a file format for the purpose of interoperability is explicitly permitted under applicable law:

  • European Union — Directive 2009/24/EC on the legal protection of computer programs, Article 6, permits decompilation and reverse engineering for interoperability purposes, notwithstanding any contractual restriction to the contrary.
  • United States — 17 U.S.C. § 1201(f) (DMCA) provides a specific exemption for reverse engineering undertaken to achieve interoperability. This is supported by established case law (Sega Enterprises Ltd. v. Accolade, Inc., 977 F.2d 1510; Sony Computer Entertainment, Inc. v. Connectix Corp., 203 F.3d 596).

File formats — as distinct from software implementations — are not protected by copyright as a matter of EU and US law (SAS Institute Inc. v. World Programming Ltd, CJEU C-406/10, 2012).

Enterprise users who require legal sign-off before adopting a dependency are encouraged to share this section with their legal team.

License

This project is licensed under the MIT License. See LICENSE for the full text.

Copyright (c) 2025 Nicolas Stefaniuk

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qvd_parser-0.1.0.tar.gz (93.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

qvd_parser-0.1.0-cp313-cp313-win_amd64.whl (3.0 MB view details)

Uploaded CPython 3.13Windows x86-64

qvd_parser-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

qvd_parser-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.4 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

qvd_parser-0.1.0-cp313-cp313-macosx_11_0_arm64.whl (3.1 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

qvd_parser-0.1.0-cp312-cp312-win_amd64.whl (3.0 MB view details)

Uploaded CPython 3.12Windows x86-64

qvd_parser-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

qvd_parser-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.4 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

qvd_parser-0.1.0-cp312-cp312-macosx_11_0_arm64.whl (3.1 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

qvd_parser-0.1.0-cp311-cp311-win_amd64.whl (3.0 MB view details)

Uploaded CPython 3.11Windows x86-64

qvd_parser-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

qvd_parser-0.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.4 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

qvd_parser-0.1.0-cp311-cp311-macosx_11_0_arm64.whl (3.1 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

File details

Details for the file qvd_parser-0.1.0.tar.gz.

File metadata

  • Download URL: qvd_parser-0.1.0.tar.gz
  • Upload date:
  • Size: 93.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qvd_parser-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1112b8ddf2ce55046a313708d19979ec57660d976dd74db8e9fa31512431a69d
MD5 0e9bbf105651f34d19180a8bd89052c6
BLAKE2b-256 329b5769e21ece672575653ca6ae75bfda3d22755b8476587db0a2931332793b

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0.tar.gz:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: qvd_parser-0.1.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 3.0 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qvd_parser-0.1.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 8c725e8af5d99ecdceb647c566bed7634cc79174e12db2ae5045c31514be4413
MD5 3b0464cd91ebae1d679bc92230e08f52
BLAKE2b-256 e3c247c28b370c2633975a6094f31e61e57afaf6a30fcd88893abaefca54ec6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp313-cp313-win_amd64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 10866fd46646189d922d54817ba3a21d0072160d0ec2ab38a8c5c21f45cf427c
MD5 9d3b6a97f7766e85e635de62287e189a
BLAKE2b-256 3176febf19064625517480456e5ad035ae91ee5802ff83f1f9da07c7855c897f

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 636dc84a3c8ccfcba8f9e08b12474f9f8b7cc60e1d4a0879476ed91778f9baae
MD5 184fb7c72270f940da594b4edbd4351f
BLAKE2b-256 63546c4f4a3ac23906952b7a41172c777a8298c8359d9fd6d07dfb4b302cc8da

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 faf2047a7fbf78f66838cee5131b8134a9aea9ad3a0afab036536921ca6065b0
MD5 58eb5ebcdbbe993d12818ddb62b22043
BLAKE2b-256 cc3d0c69f13f8b60e230be8a4c223b80e56db3d1fca866dbfbda59c9a33cc723

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: qvd_parser-0.1.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 3.0 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qvd_parser-0.1.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 93bcf5a54e9bb0528f18397e37852bafa05d5f23c9b3c75ec75c6379bd822eb1
MD5 5a01a9f767e0dbb3acf92ecfd20bdb2c
BLAKE2b-256 5cf0daaccc48543f37b9a8e2ff9a30c1d045a629bc13975d1fdf2257411446ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp312-cp312-win_amd64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cd39ae6c072a124ac53a9f60e9b896dd4533665d35b8660153de431fe965bdf9
MD5 01e05f83aac9904b6ffb13f6edc31121
BLAKE2b-256 7a2afa814e9c4bdb7496627cdd9950841943db2618648ef9cf6046c1534b97d3

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 55ee2c7318b45e91c7d5145c019a0c1acf97f9ad51ecd74e6b404158c7fdca7a
MD5 49264e71710a88b8c80f55efdb539bcb
BLAKE2b-256 c7f097a5ed4af557e752ed61c6a0678271f546cbb8ac413fff59157d24f2e7b9

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b239eb8c24596a0f85e741ad1576ca40625d79c993d420a93975742826a7046b
MD5 ae45521cd1a5c567c4651f57545b0b26
BLAKE2b-256 342501664ea9b76ac040cd0702c0d290b4809861519d651f97a05e528d58c27c

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: qvd_parser-0.1.0-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 3.0 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qvd_parser-0.1.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 590ba5ef8e29e4b2377a5ed47d48523bccee898a57a88cce63ac648d442fd02e
MD5 679a5ad41d28b52f8672f1387321f88a
BLAKE2b-256 0287585509cbbfc903e9282e5188a1394d425b3d805e6e6ea98b5716b7ac063a

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp311-cp311-win_amd64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 acbbc4946fac8b79c4ba07aa159368993d8134898bdec57c62403b1ebf7fe728
MD5 e908d90e4cadb4828a027ee38c946010
BLAKE2b-256 e88e12700a4c5bc1eb725981ad94cff561655d4c96f96f280ed0f032ca5c68ef

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 bfc633c47e31a3a9c45a3a9e4c1dd47b83b2b71b4e35e24225c55630d6e76107
MD5 7f8e82b8bdca5ca7a877238d38e7ad87
BLAKE2b-256 856f40fbe6eb63533fc33922796b1ea83a12c245aee6c122d80dc1e07e6dceed

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qvd_parser-0.1.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for qvd_parser-0.1.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 efd82e8041f8f7013a6cc3b423fecb3a60b7f117e6c482f97a1095b8974d1f78
MD5 757c2a6c1032aa7dcda82b03561d6504
BLAKE2b-256 922009aa9bfcbe9d09d4c878c73e6b11fae1e5b64e1c5757b6ef3b2bb337eb0d

See more details on using hashes here.

Provenance

The following attestation bundles were made for qvd_parser-0.1.0-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: publish.yml on nicolasstefaniuk/qvd-parser

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page