sqlce 0.1.1__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlce-0.1.3/.github/workflows/release.yml +162 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/.gitignore +1 -0
- sqlce-0.1.3/PKG-INFO +87 -0
- sqlce-0.1.3/README.md +56 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/CMakeLists.txt +16 -1
- sqlce-0.1.3/core/include/sdf/domain/DateTimeValue.hpp +36 -0
- sqlce-0.1.3/core/src/domain/DateTimeValue.cpp +135 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/RowDecoder.cpp +4 -4
- {sqlce-0.1.1 → sqlce-0.1.3}/docs/build.txt +2 -2
- sqlce-0.1.3/docs/ru/RuReadme.md +58 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/pyproject.toml +16 -1
- {sqlce-0.1.1 → sqlce-0.1.3}/requirements.txt +3 -0
- sqlce-0.1.3/tests/__init__.py +0 -0
- sqlce-0.1.3/tests/conftest.py +80 -0
- sqlce-0.1.3/tests/edge_case_tables.py +209 -0
- sqlce-0.1.3/tests/sample_tables.py +15 -0
- sqlce-0.1.3/tests/sdf_factory.py +253 -0
- sqlce-0.1.3/tests/table_spec.py +170 -0
- sqlce-0.1.3/tests/test_sdf40_edge_cases.py +120 -0
- sqlce-0.1.3/tests/test_sdf40_encrypted_engine_default.py +49 -0
- sqlce-0.1.3/tests/test_sdf40_encrypted_platform_default.py +49 -0
- sqlce-0.1.3/tests/test_sdf40_plain.py +37 -0
- sqlce-0.1.1/.github/workflows/release.yml +0 -78
- sqlce-0.1.1/PKG-INFO +0 -48
- sqlce-0.1.1/README.md +0 -25
- sqlce-0.1.1/core/include/sdf/domain/DateTimeValue.hpp +0 -36
- sqlce-0.1.1/core/src/domain/DateTimeValue.cpp +0 -107
- {sqlce-0.1.1 → sqlce-0.1.3}/CMakeLists.txt +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/LICENSE +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/bindings/CMakeLists.txt +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/bindings/src/module.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/bindings/src/value_convert.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/bindings/src/value_convert.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/application/ColumnSchema.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/application/SdfDatabase.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/ColumnDef.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/ColumnType.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/ColumnValue.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/EncryptionMode.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/Guid.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/IPageCipher.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/IPageStorage.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/NumericValue.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/PageLayout.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/Row.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/TableDef.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/AesCbc.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/BinaryReader.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/FileStorage.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/PageView.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/Sha256.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/Sha512.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/CatalogPageScanner.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/CatalogRow.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/CatalogRowDecoder.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/CatalogSchema.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/ICatalogPageScanner.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/ICatalogRowDecoder.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/ILobChainRegistry.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/IRowDecoder.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/ITableCatalogBuilder.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/LobChainRegistry.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/RowDecoder.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/SdfPageCipher.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/TableCatalogBuilder.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/TextDecoder.hpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/main.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/application/SdfDatabase.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/ColumnDef.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/ColumnType.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/ColumnValue.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/Guid.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/NumericValue.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/Row.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/TableDef.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/AesCbc.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/BinaryReader.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/FileStorage.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/PageView.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/Sha256.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/Sha512.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/CatalogPageScanner.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/CatalogRow.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/CatalogRowDecoder.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/CatalogSchema.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/LobChainRegistry.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/SdfPageCipher.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/TableCatalogBuilder.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/TextDecoder.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/tests/CMakeLists.txt +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/core/tests/count_test.cpp +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/docs/CONVENTIONS.md +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/docs/en/cpp.md +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/docs/en/python.md +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/docs/ru/cpp.md +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/docs/ru/python.md +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/main.py +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/python/sqlce/__init__.py +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/python/sqlce/__init__.pyi +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/python/sqlce/_sdf_native.pyi +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/research/catalog_parse/sdf_pages.py +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/research/catalog_parse/syscatalog.py +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/research/sdf_d3.py +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/research/sdf_d4.py +0 -0
- {sqlce-0.1.1 → sqlce-0.1.3}/research/sdf_reader.py +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
name: Build and release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
jobs:
|
|
8
|
+
build-linux:
|
|
9
|
+
name: Build wheel (Linux)
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Build wheels
|
|
15
|
+
uses: pypa/cibuildwheel@v2.21.3
|
|
16
|
+
env:
|
|
17
|
+
CIBW_ARCHS_LINUX: x86_64
|
|
18
|
+
CIBW_BUILD_VERBOSITY: 1
|
|
19
|
+
|
|
20
|
+
- name: Build sdist
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip build
|
|
23
|
+
python -m build --sdist
|
|
24
|
+
|
|
25
|
+
- name: Collect wheels and sdist into one flat folder
|
|
26
|
+
run: |
|
|
27
|
+
mkdir -p collected
|
|
28
|
+
cp wheelhouse/*.whl collected/
|
|
29
|
+
cp dist/*.tar.gz collected/
|
|
30
|
+
|
|
31
|
+
- uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist-linux
|
|
34
|
+
path: collected/*
|
|
35
|
+
|
|
36
|
+
build-windows:
|
|
37
|
+
name: Build wheel (Windows)
|
|
38
|
+
runs-on: windows-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Build wheels
|
|
43
|
+
uses: pypa/cibuildwheel@v2.21.3
|
|
44
|
+
env:
|
|
45
|
+
CIBW_ARCHS_WINDOWS: AMD64
|
|
46
|
+
CIBW_BUILD_VERBOSITY: 1
|
|
47
|
+
CIBW_CMAKE_ARGS: "-DSDF_BUILD_PYTHON_BINDINGS=ON"
|
|
48
|
+
|
|
49
|
+
- uses: actions/upload-artifact@v4
|
|
50
|
+
with:
|
|
51
|
+
name: dist-windows
|
|
52
|
+
path: wheelhouse/*
|
|
53
|
+
|
|
54
|
+
build-cpp-linux:
|
|
55
|
+
name: Build C++ library (Linux, ${{ matrix.link }})
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
strategy:
|
|
58
|
+
matrix:
|
|
59
|
+
link: [static, shared]
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
|
|
63
|
+
- name: Configure CMake
|
|
64
|
+
run: |
|
|
65
|
+
cmake -S . -B build \
|
|
66
|
+
-DCMAKE_BUILD_TYPE=Release \
|
|
67
|
+
-DBUILD_SHARED_LIBS=${{ matrix.link == 'shared' && 'ON' || 'OFF' }}
|
|
68
|
+
|
|
69
|
+
- name: Build libsdf
|
|
70
|
+
run: cmake --build build --target libsdf -j
|
|
71
|
+
|
|
72
|
+
- name: Package library + headers
|
|
73
|
+
run: |
|
|
74
|
+
version="${GITHUB_REF_NAME#v}"
|
|
75
|
+
pkg="libsdf-${version}-linux-x86_64-${{ matrix.link }}"
|
|
76
|
+
mkdir -p "$pkg/lib" "$pkg/include"
|
|
77
|
+
cp build/dist/lib/libsdf.* "$pkg/lib/"
|
|
78
|
+
cp -r core/include/. "$pkg/include/"
|
|
79
|
+
tar -czf "${pkg}.tar.gz" "$pkg"
|
|
80
|
+
|
|
81
|
+
- uses: actions/upload-artifact@v4
|
|
82
|
+
with:
|
|
83
|
+
name: cpp-linux-${{ matrix.link }}
|
|
84
|
+
path: libsdf-*.tar.gz
|
|
85
|
+
|
|
86
|
+
build-cpp-windows:
|
|
87
|
+
name: Build C++ library (Windows, ${{ matrix.link }})
|
|
88
|
+
runs-on: windows-latest
|
|
89
|
+
strategy:
|
|
90
|
+
matrix:
|
|
91
|
+
link: [static, shared]
|
|
92
|
+
steps:
|
|
93
|
+
- uses: actions/checkout@v4
|
|
94
|
+
|
|
95
|
+
- name: Configure CMake
|
|
96
|
+
shell: pwsh
|
|
97
|
+
run: |
|
|
98
|
+
$shared = if ("${{ matrix.link }}" -eq "shared") { "ON" } else { "OFF" }
|
|
99
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=$shared
|
|
100
|
+
|
|
101
|
+
- name: Build libsdf
|
|
102
|
+
run: cmake --build build --target libsdf --config Release -j
|
|
103
|
+
|
|
104
|
+
- name: Package library + headers
|
|
105
|
+
shell: pwsh
|
|
106
|
+
run: |
|
|
107
|
+
$version = "${env:GITHUB_REF_NAME}" -replace '^v', ''
|
|
108
|
+
$pkg = "libsdf-$version-windows-x86_64-${{ matrix.link }}"
|
|
109
|
+
New-Item -ItemType Directory -Force -Path "$pkg/lib" | Out-Null
|
|
110
|
+
New-Item -ItemType Directory -Force -Path "$pkg/include" | Out-Null
|
|
111
|
+
Copy-Item "build/dist/lib/libsdf.*" "$pkg/lib/"
|
|
112
|
+
Copy-Item -Recurse "core/include/*" "$pkg/include/"
|
|
113
|
+
Compress-Archive -Path $pkg -DestinationPath "$pkg.zip"
|
|
114
|
+
|
|
115
|
+
- uses: actions/upload-artifact@v4
|
|
116
|
+
with:
|
|
117
|
+
name: cpp-windows-${{ matrix.link }}
|
|
118
|
+
path: libsdf-*.zip
|
|
119
|
+
|
|
120
|
+
publish:
|
|
121
|
+
name: Publish to PyPI
|
|
122
|
+
needs: [build-linux, build-windows]
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
environment: pypi
|
|
125
|
+
permissions:
|
|
126
|
+
id-token: write
|
|
127
|
+
steps:
|
|
128
|
+
- uses: actions/download-artifact@v4
|
|
129
|
+
with:
|
|
130
|
+
pattern: dist-*
|
|
131
|
+
path: dist
|
|
132
|
+
merge-multiple: true
|
|
133
|
+
|
|
134
|
+
- name: Publish to PyPI
|
|
135
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
136
|
+
with:
|
|
137
|
+
packages-dir: dist
|
|
138
|
+
|
|
139
|
+
publish-release:
|
|
140
|
+
name: Publish GitHub Release
|
|
141
|
+
needs: [build-cpp-linux, build-cpp-windows, build-linux, build-windows]
|
|
142
|
+
runs-on: ubuntu-latest
|
|
143
|
+
permissions:
|
|
144
|
+
contents: write
|
|
145
|
+
steps:
|
|
146
|
+
- uses: actions/download-artifact@v4
|
|
147
|
+
with:
|
|
148
|
+
pattern: cpp-*
|
|
149
|
+
path: release-assets
|
|
150
|
+
merge-multiple: true
|
|
151
|
+
|
|
152
|
+
- uses: actions/download-artifact@v4
|
|
153
|
+
with:
|
|
154
|
+
pattern: dist-*
|
|
155
|
+
path: release-assets
|
|
156
|
+
merge-multiple: true
|
|
157
|
+
|
|
158
|
+
- name: Create GitHub Release
|
|
159
|
+
uses: softprops/action-gh-release@v2
|
|
160
|
+
with:
|
|
161
|
+
files: release-assets/*
|
|
162
|
+
generate_release_notes: true
|
sqlce-0.1.3/PKG-INFO
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: sqlce
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Cross-platform lib for reading SQL Server CE (.sdf)
|
|
5
|
+
Author-Email: boykopovar <boykopovar@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: C++
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Project-URL: Homepage, https://github.com/boykopovar/sqlce
|
|
20
|
+
Project-URL: Issues, https://github.com/boykopovar/sqlce/issues
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: scikit-build-core>=0.10; extra == "dev"
|
|
24
|
+
Requires-Dist: pybind11>=2.12; extra == "dev"
|
|
25
|
+
Requires-Dist: build; extra == "dev"
|
|
26
|
+
Requires-Dist: twine; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
29
|
+
Requires-Dist: pythonnet; extra == "dev"
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# sqlce
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+

|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
## [Ru README](docs/ru/RuReadme.md)
|
|
41
|
+
|
|
42
|
+
Reverse engineering of the `.sdf` (SQL Server Compact Edition 4.0) binary file format.
|
|
43
|
+
|
|
44
|
+
Analysis of the format's internal structure based on decompiled SQL CE components. The goal is to provide a cross-platform, runtime-independent implementation for reading `.sdf` files.
|
|
45
|
+
|
|
46
|
+
> Intended solely for interoperability with existing `.sdf` files. Does not contain any original product source code.
|
|
47
|
+
|
|
48
|
+
- `core/` — C++ parser implementation
|
|
49
|
+
- `bindings/` — Bindings for the C++ library to other languages
|
|
50
|
+
- `python/sqlce/` — Python API
|
|
51
|
+
- `research/` — Experimental scripts
|
|
52
|
+
|
|
53
|
+
### Example
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install sqlce
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from sqlce import SdfDatabase
|
|
61
|
+
|
|
62
|
+
db = SdfDatabase("example.sdf", password="secret123")
|
|
63
|
+
|
|
64
|
+
for table_name in db.list_tables():
|
|
65
|
+
print(table_name, db.table_schema(table_name))
|
|
66
|
+
for row in db.read_table(table_name):
|
|
67
|
+
print(row)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The current implementation supports reading the list of tables, table schemas, and table contents (including password-protected databases). It is implemented in C++.
|
|
71
|
+
|
|
72
|
+
### Testing
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pytest
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The tests use a locally installed SQL CE engine (via ODBC) to generate `.sdf` files containing predefined data and verify that the parser produces identical results. Therefore, the test suite can only be run on Windows with SQL Server Compact 4.0 installed. Test files are created in `tests/sdf/` and are automatically removed before and after each test run.
|
|
79
|
+
|
|
80
|
+
### Documentation
|
|
81
|
+
|
|
82
|
+
The documentation is currently concise but includes usage examples and tests.
|
|
83
|
+
|
|
84
|
+
[Documentation](docs/en/python.md)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
[Code Style Conventions](docs/CONVENTIONS.md) (Pull requests are welcome.)
|
sqlce-0.1.3/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# sqlce
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## [Ru README](docs/ru/RuReadme.md)
|
|
10
|
+
|
|
11
|
+
Reverse engineering of the `.sdf` (SQL Server Compact Edition 4.0) binary file format.
|
|
12
|
+
|
|
13
|
+
Analysis of the format's internal structure based on decompiled SQL CE components. The goal is to provide a cross-platform, runtime-independent implementation for reading `.sdf` files.
|
|
14
|
+
|
|
15
|
+
> Intended solely for interoperability with existing `.sdf` files. Does not contain any original product source code.
|
|
16
|
+
|
|
17
|
+
- `core/` — C++ parser implementation
|
|
18
|
+
- `bindings/` — Bindings for the C++ library to other languages
|
|
19
|
+
- `python/sqlce/` — Python API
|
|
20
|
+
- `research/` — Experimental scripts
|
|
21
|
+
|
|
22
|
+
### Example
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install sqlce
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from sqlce import SdfDatabase
|
|
30
|
+
|
|
31
|
+
db = SdfDatabase("example.sdf", password="secret123")
|
|
32
|
+
|
|
33
|
+
for table_name in db.list_tables():
|
|
34
|
+
print(table_name, db.table_schema(table_name))
|
|
35
|
+
for row in db.read_table(table_name):
|
|
36
|
+
print(row)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The current implementation supports reading the list of tables, table schemas, and table contents (including password-protected databases). It is implemented in C++.
|
|
40
|
+
|
|
41
|
+
### Testing
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pytest
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The tests use a locally installed SQL CE engine (via ODBC) to generate `.sdf` files containing predefined data and verify that the parser produces identical results. Therefore, the test suite can only be run on Windows with SQL Server Compact 4.0 installed. Test files are created in `tests/sdf/` and are automatically removed before and after each test run.
|
|
48
|
+
|
|
49
|
+
### Documentation
|
|
50
|
+
|
|
51
|
+
The documentation is currently concise but includes usage examples and tests.
|
|
52
|
+
|
|
53
|
+
[Documentation](docs/en/python.md)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
[Code Style Conventions](docs/CONVENTIONS.md) (Pull requests are welcome.)
|
|
@@ -36,7 +36,14 @@ add_library(libsdf
|
|
|
36
36
|
src/application/SdfDatabase.cpp
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
-
set_target_properties(libsdf PROPERTIES
|
|
39
|
+
set_target_properties(libsdf PROPERTIES
|
|
40
|
+
PREFIX ""
|
|
41
|
+
OUTPUT_NAME "libsdf"
|
|
42
|
+
POSITION_INDEPENDENT_CODE ON
|
|
43
|
+
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/dist/lib"
|
|
44
|
+
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/dist/lib"
|
|
45
|
+
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/dist/lib"
|
|
46
|
+
)
|
|
40
47
|
|
|
41
48
|
target_include_directories(libsdf PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
|
42
49
|
|
|
@@ -57,6 +64,14 @@ if(MSVC)
|
|
|
57
64
|
target_compile_definitions(sdf_dump PRIVATE NOMINMAX _CRT_SECURE_NO_WARNINGS)
|
|
58
65
|
else()
|
|
59
66
|
target_compile_options(sdf_dump PRIVATE -Wall -Wextra)
|
|
67
|
+
if(MINGW)
|
|
68
|
+
target_link_options(sdf_dump PRIVATE
|
|
69
|
+
-static
|
|
70
|
+
-static-libgcc
|
|
71
|
+
-static-libstdc++
|
|
72
|
+
-Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive
|
|
73
|
+
)
|
|
74
|
+
endif()
|
|
60
75
|
endif()
|
|
61
76
|
|
|
62
77
|
enable_testing()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#ifndef SDF_DOMAIN_DATE_TIME_VALUE_HPP
|
|
2
|
+
#define SDF_DOMAIN_DATE_TIME_VALUE_HPP
|
|
3
|
+
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
namespace sdf::domain
|
|
8
|
+
{
|
|
9
|
+
|
|
10
|
+
struct CalendarDate
|
|
11
|
+
{
|
|
12
|
+
int year;
|
|
13
|
+
int month;
|
|
14
|
+
int day;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
class DateTimeValue
|
|
18
|
+
{
|
|
19
|
+
public:
|
|
20
|
+
DateTimeValue();
|
|
21
|
+
DateTimeValue(std::int32_t daysSinceEpoch, std::uint32_t ticksSinceMidnight);
|
|
22
|
+
|
|
23
|
+
std::uint32_t MillisecondsSinceMidnight() const;
|
|
24
|
+
std::int32_t DaysSinceEpoch() const;
|
|
25
|
+
CalendarDate Date() const;
|
|
26
|
+
|
|
27
|
+
std::string ToString() const;
|
|
28
|
+
|
|
29
|
+
private:
|
|
30
|
+
std::int32_t _daysSinceEpoch;
|
|
31
|
+
std::uint32_t _ticksSinceMidnight;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#endif
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#include "sdf/domain/DateTimeValue.hpp"
|
|
2
|
+
|
|
3
|
+
#include <cstdio>
|
|
4
|
+
|
|
5
|
+
namespace sdf::domain
|
|
6
|
+
{
|
|
7
|
+
|
|
8
|
+
namespace
|
|
9
|
+
{
|
|
10
|
+
|
|
11
|
+
constexpr int EpochYear = 1900;
|
|
12
|
+
constexpr int EpochMonth = 1;
|
|
13
|
+
constexpr int EpochDay = 1;
|
|
14
|
+
|
|
15
|
+
bool IsLeapYear(int year)
|
|
16
|
+
{
|
|
17
|
+
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
int DaysInMonth(int year, int month)
|
|
21
|
+
{
|
|
22
|
+
static constexpr int daysByMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
23
|
+
if (month == 2 && IsLeapYear(year))
|
|
24
|
+
{
|
|
25
|
+
return 29;
|
|
26
|
+
}
|
|
27
|
+
return daysByMonth[month - 1];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
CalendarDate AddDays(int year, int month, int day, std::int32_t daysToAdd)
|
|
31
|
+
{
|
|
32
|
+
if (daysToAdd >= 0)
|
|
33
|
+
{
|
|
34
|
+
std::uint32_t remaining = static_cast<std::uint32_t>(daysToAdd);
|
|
35
|
+
while (remaining > 0)
|
|
36
|
+
{
|
|
37
|
+
const int daysLeftInMonth = DaysInMonth(year, month) - day;
|
|
38
|
+
if (static_cast<std::uint32_t>(daysLeftInMonth) >= remaining)
|
|
39
|
+
{
|
|
40
|
+
day += static_cast<int>(remaining);
|
|
41
|
+
remaining = 0;
|
|
42
|
+
}
|
|
43
|
+
else
|
|
44
|
+
{
|
|
45
|
+
remaining -= static_cast<std::uint32_t>(daysLeftInMonth) + 1;
|
|
46
|
+
day = 1;
|
|
47
|
+
month += 1;
|
|
48
|
+
if (month > 12)
|
|
49
|
+
{
|
|
50
|
+
month = 1;
|
|
51
|
+
year += 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
{
|
|
58
|
+
std::uint32_t remaining = static_cast<std::uint32_t>(-daysToAdd);
|
|
59
|
+
while (remaining > 0)
|
|
60
|
+
{
|
|
61
|
+
const int daysIntoMonth = day - 1;
|
|
62
|
+
if (static_cast<std::uint32_t>(daysIntoMonth) >= remaining)
|
|
63
|
+
{
|
|
64
|
+
day -= static_cast<int>(remaining);
|
|
65
|
+
remaining = 0;
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
{
|
|
69
|
+
remaining -= static_cast<std::uint32_t>(daysIntoMonth) + 1;
|
|
70
|
+
month -= 1;
|
|
71
|
+
if (month < 1)
|
|
72
|
+
{
|
|
73
|
+
month = 12;
|
|
74
|
+
year -= 1;
|
|
75
|
+
}
|
|
76
|
+
day = DaysInMonth(year, month);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return CalendarDate{year, month, day};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
DateTimeValue::DateTimeValue() : _daysSinceEpoch(0), _ticksSinceMidnight(0)
|
|
86
|
+
{
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
DateTimeValue::DateTimeValue(std::int32_t daysSinceEpoch, std::uint32_t ticksSinceMidnight)
|
|
90
|
+
: _daysSinceEpoch(daysSinceEpoch), _ticksSinceMidnight(ticksSinceMidnight)
|
|
91
|
+
{
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
std::uint32_t DateTimeValue::MillisecondsSinceMidnight() const
|
|
95
|
+
{
|
|
96
|
+
return static_cast<std::uint32_t>((static_cast<std::uint64_t>(_ticksSinceMidnight) * 10ULL + 1ULL) / 3ULL);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
std::int32_t DateTimeValue::DaysSinceEpoch() const
|
|
100
|
+
{
|
|
101
|
+
return _daysSinceEpoch;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
CalendarDate DateTimeValue::Date() const
|
|
105
|
+
{
|
|
106
|
+
return AddDays(EpochYear, EpochMonth, EpochDay, _daysSinceEpoch);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
std::string DateTimeValue::ToString() const
|
|
110
|
+
{
|
|
111
|
+
const CalendarDate date = Date();
|
|
112
|
+
|
|
113
|
+
const std::uint32_t milliseconds = MillisecondsSinceMidnight();
|
|
114
|
+
const std::uint32_t totalSeconds = milliseconds / 1000;
|
|
115
|
+
const std::uint32_t ms = milliseconds % 1000;
|
|
116
|
+
const std::uint32_t hours = (totalSeconds / 3600) % 24;
|
|
117
|
+
const std::uint32_t minutes = (totalSeconds / 60) % 60;
|
|
118
|
+
const std::uint32_t seconds = totalSeconds % 60;
|
|
119
|
+
|
|
120
|
+
char buffer[32];
|
|
121
|
+
std::snprintf(
|
|
122
|
+
buffer,
|
|
123
|
+
sizeof(buffer),
|
|
124
|
+
"%04d-%02d-%02d %02u:%02u:%02u.%03u",
|
|
125
|
+
date.year,
|
|
126
|
+
date.month,
|
|
127
|
+
date.day,
|
|
128
|
+
hours,
|
|
129
|
+
minutes,
|
|
130
|
+
seconds,
|
|
131
|
+
ms);
|
|
132
|
+
return std::string(buffer);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
@@ -232,9 +232,9 @@ domain::ColumnValue RowDecoder::DecodeFixed(const domain::ColumnDef& column, std
|
|
|
232
232
|
|
|
233
233
|
case domain::ColumnType::DateTime:
|
|
234
234
|
{
|
|
235
|
-
const std::uint32_t
|
|
236
|
-
const std::
|
|
237
|
-
return domain::ColumnValue(domain::ColumnValueStorage(domain::DateTimeValue(
|
|
235
|
+
const std::uint32_t ticks = infrastructure::ReadUInt32LE(chunk, 0);
|
|
236
|
+
const std::int32_t days = infrastructure::ReadInt32LE(chunk, 4);
|
|
237
|
+
return domain::ColumnValue(domain::ColumnValueStorage(domain::DateTimeValue(days, ticks)));
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
case domain::ColumnType::Money:
|
|
@@ -318,4 +318,4 @@ domain::ColumnValue RowDecoder::DecodeLob(
|
|
|
318
318
|
return domain::ColumnValue(domain::ColumnValueStorage(payload));
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
}
|
|
321
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
pip install -e . -v --config-settings=cmake.args="-G Ninja;-DCMAKE_CXX_COMPILER=g++;-DCMAKE_C_COMPILER=gcc;-DSDF_BUILD_PYTHON_BINDINGS=ON"
|
|
2
2
|
python -m build --wheel -C cmake.args="-G Ninja;-DCMAKE_CXX_COMPILER=g++;-DCMAKE_C_COMPILER=gcc;-DSDF_BUILD_PYTHON_BINDINGS=ON"
|
|
3
3
|
|
|
4
|
-
git tag v0.1.
|
|
5
|
-
git push origin v0.1.
|
|
4
|
+
git tag v0.1.2
|
|
5
|
+
git push origin v0.1.2
|
|
6
6
|
|
|
7
7
|
git tag -d v0.1.0
|
|
8
8
|
git push origin --delete v0.1.0
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# sqlce
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## [En README](../../README.md)
|
|
10
|
+
|
|
11
|
+
Реверс-инжиниринг бинарного формата `.sdf` (SQL Server Compact Edition 4.0).
|
|
12
|
+
|
|
13
|
+
Анализ внутреннего устройства формата по декомпилированным компонентам SQL CE. Цель - предоставить кроссплатформенную и независимую от рантайма реализацию чтения `.sdf`.
|
|
14
|
+
|
|
15
|
+
> Исключительно для интероперабельности с существующими файлами `.sdf` формата. Не содержит оригинальный код продукта.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
- `core/` - реализация парсера на C++
|
|
19
|
+
- `bindings/` - Биндинги C++ библиотеки к другим языкам
|
|
20
|
+
- `python/sqlce/` - Python API
|
|
21
|
+
- `research/` - Экспериментальные скрипты
|
|
22
|
+
|
|
23
|
+
### Пример
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install sqlce
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from sqlce import SdfDatabase
|
|
31
|
+
|
|
32
|
+
db = SdfDatabase("example.sdf", password="secret123")
|
|
33
|
+
|
|
34
|
+
for table_name in db.list_tables():
|
|
35
|
+
print(table_name, db.table_schema(table_name))
|
|
36
|
+
for row in db.read_table(table_name):
|
|
37
|
+
print(row)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Текущая [реализация](main.py) поддерживает чтение (поддерживается передача пароля) списка таблиц, структуры таблиц, содержимого записей. Использует c++.
|
|
41
|
+
|
|
42
|
+
### Тестирование
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pytest
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Тесты используют установленный на устройстве SQL CE движок (через ODBC) для генерации `.sdf`-файлов с заранее известными данными и сверяют их с результатом чтения реализованным парсером - так что запуск возможен только на Windows с установленным SQL Server Compact 4.0. Файлы создаются в `tests/sdf/` и автоматически удаляются в начале и после каждого прогона.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### Документация
|
|
52
|
+
|
|
53
|
+
На данный момент документация краткая, но есть примеры и тесты.
|
|
54
|
+
|
|
55
|
+
[Документация](python.md)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
[Соглашения по написанию кода](docs/CONVENTIONS.md) (PR приветствуются)
|
|
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sqlce"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.3"
|
|
8
8
|
description = "Cross-platform lib for reading SQL Server CE (.sdf)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -52,3 +52,18 @@ archs = ["x86_64"]
|
|
|
52
52
|
|
|
53
53
|
[tool.cibuildwheel.windows]
|
|
54
54
|
archs = ["AMD64"]
|
|
55
|
+
|
|
56
|
+
[project.optional-dependencies]
|
|
57
|
+
dev = [
|
|
58
|
+
"scikit-build-core>=0.10",
|
|
59
|
+
"pybind11>=2.12",
|
|
60
|
+
"build",
|
|
61
|
+
"twine",
|
|
62
|
+
"pytest",
|
|
63
|
+
"pytest-cov",
|
|
64
|
+
"pythonnet"
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
addopts = "--cov=sqlce --cov-report=term-missing"
|
|
69
|
+
testpaths = ["tests"]
|
|
File without changes
|