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.
Files changed (105) hide show
  1. sqlce-0.1.3/.github/workflows/release.yml +162 -0
  2. {sqlce-0.1.1 → sqlce-0.1.3}/.gitignore +1 -0
  3. sqlce-0.1.3/PKG-INFO +87 -0
  4. sqlce-0.1.3/README.md +56 -0
  5. {sqlce-0.1.1 → sqlce-0.1.3}/core/CMakeLists.txt +16 -1
  6. sqlce-0.1.3/core/include/sdf/domain/DateTimeValue.hpp +36 -0
  7. sqlce-0.1.3/core/src/domain/DateTimeValue.cpp +135 -0
  8. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/RowDecoder.cpp +4 -4
  9. {sqlce-0.1.1 → sqlce-0.1.3}/docs/build.txt +2 -2
  10. sqlce-0.1.3/docs/ru/RuReadme.md +58 -0
  11. {sqlce-0.1.1 → sqlce-0.1.3}/pyproject.toml +16 -1
  12. {sqlce-0.1.1 → sqlce-0.1.3}/requirements.txt +3 -0
  13. sqlce-0.1.3/tests/__init__.py +0 -0
  14. sqlce-0.1.3/tests/conftest.py +80 -0
  15. sqlce-0.1.3/tests/edge_case_tables.py +209 -0
  16. sqlce-0.1.3/tests/sample_tables.py +15 -0
  17. sqlce-0.1.3/tests/sdf_factory.py +253 -0
  18. sqlce-0.1.3/tests/table_spec.py +170 -0
  19. sqlce-0.1.3/tests/test_sdf40_edge_cases.py +120 -0
  20. sqlce-0.1.3/tests/test_sdf40_encrypted_engine_default.py +49 -0
  21. sqlce-0.1.3/tests/test_sdf40_encrypted_platform_default.py +49 -0
  22. sqlce-0.1.3/tests/test_sdf40_plain.py +37 -0
  23. sqlce-0.1.1/.github/workflows/release.yml +0 -78
  24. sqlce-0.1.1/PKG-INFO +0 -48
  25. sqlce-0.1.1/README.md +0 -25
  26. sqlce-0.1.1/core/include/sdf/domain/DateTimeValue.hpp +0 -36
  27. sqlce-0.1.1/core/src/domain/DateTimeValue.cpp +0 -107
  28. {sqlce-0.1.1 → sqlce-0.1.3}/CMakeLists.txt +0 -0
  29. {sqlce-0.1.1 → sqlce-0.1.3}/LICENSE +0 -0
  30. {sqlce-0.1.1 → sqlce-0.1.3}/bindings/CMakeLists.txt +0 -0
  31. {sqlce-0.1.1 → sqlce-0.1.3}/bindings/src/module.cpp +0 -0
  32. {sqlce-0.1.1 → sqlce-0.1.3}/bindings/src/value_convert.cpp +0 -0
  33. {sqlce-0.1.1 → sqlce-0.1.3}/bindings/src/value_convert.hpp +0 -0
  34. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/application/ColumnSchema.hpp +0 -0
  35. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/application/SdfDatabase.hpp +0 -0
  36. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/ColumnDef.hpp +0 -0
  37. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/ColumnType.hpp +0 -0
  38. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/ColumnValue.hpp +0 -0
  39. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/EncryptionMode.hpp +0 -0
  40. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/Guid.hpp +0 -0
  41. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/IPageCipher.hpp +0 -0
  42. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/IPageStorage.hpp +0 -0
  43. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/NumericValue.hpp +0 -0
  44. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/PageLayout.hpp +0 -0
  45. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/Row.hpp +0 -0
  46. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/domain/TableDef.hpp +0 -0
  47. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/AesCbc.hpp +0 -0
  48. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/BinaryReader.hpp +0 -0
  49. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/FileStorage.hpp +0 -0
  50. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/PageView.hpp +0 -0
  51. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/Sha256.hpp +0 -0
  52. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/infrastructure/Sha512.hpp +0 -0
  53. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/CatalogPageScanner.hpp +0 -0
  54. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/CatalogRow.hpp +0 -0
  55. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/CatalogRowDecoder.hpp +0 -0
  56. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/CatalogSchema.hpp +0 -0
  57. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/ICatalogPageScanner.hpp +0 -0
  58. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/ICatalogRowDecoder.hpp +0 -0
  59. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/ILobChainRegistry.hpp +0 -0
  60. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/IRowDecoder.hpp +0 -0
  61. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/ITableCatalogBuilder.hpp +0 -0
  62. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/LobChainRegistry.hpp +0 -0
  63. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/RowDecoder.hpp +0 -0
  64. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/SdfPageCipher.hpp +0 -0
  65. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/TableCatalogBuilder.hpp +0 -0
  66. {sqlce-0.1.1 → sqlce-0.1.3}/core/include/sdf/parsing/TextDecoder.hpp +0 -0
  67. {sqlce-0.1.1 → sqlce-0.1.3}/core/main.cpp +0 -0
  68. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/application/SdfDatabase.cpp +0 -0
  69. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/ColumnDef.cpp +0 -0
  70. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/ColumnType.cpp +0 -0
  71. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/ColumnValue.cpp +0 -0
  72. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/Guid.cpp +0 -0
  73. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/NumericValue.cpp +0 -0
  74. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/Row.cpp +0 -0
  75. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/domain/TableDef.cpp +0 -0
  76. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/AesCbc.cpp +0 -0
  77. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/BinaryReader.cpp +0 -0
  78. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/FileStorage.cpp +0 -0
  79. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/PageView.cpp +0 -0
  80. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/Sha256.cpp +0 -0
  81. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/infrastructure/Sha512.cpp +0 -0
  82. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/CatalogPageScanner.cpp +0 -0
  83. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/CatalogRow.cpp +0 -0
  84. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/CatalogRowDecoder.cpp +0 -0
  85. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/CatalogSchema.cpp +0 -0
  86. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/LobChainRegistry.cpp +0 -0
  87. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/SdfPageCipher.cpp +0 -0
  88. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/TableCatalogBuilder.cpp +0 -0
  89. {sqlce-0.1.1 → sqlce-0.1.3}/core/src/parsing/TextDecoder.cpp +0 -0
  90. {sqlce-0.1.1 → sqlce-0.1.3}/core/tests/CMakeLists.txt +0 -0
  91. {sqlce-0.1.1 → sqlce-0.1.3}/core/tests/count_test.cpp +0 -0
  92. {sqlce-0.1.1 → sqlce-0.1.3}/docs/CONVENTIONS.md +0 -0
  93. {sqlce-0.1.1 → sqlce-0.1.3}/docs/en/cpp.md +0 -0
  94. {sqlce-0.1.1 → sqlce-0.1.3}/docs/en/python.md +0 -0
  95. {sqlce-0.1.1 → sqlce-0.1.3}/docs/ru/cpp.md +0 -0
  96. {sqlce-0.1.1 → sqlce-0.1.3}/docs/ru/python.md +0 -0
  97. {sqlce-0.1.1 → sqlce-0.1.3}/main.py +0 -0
  98. {sqlce-0.1.1 → sqlce-0.1.3}/python/sqlce/__init__.py +0 -0
  99. {sqlce-0.1.1 → sqlce-0.1.3}/python/sqlce/__init__.pyi +0 -0
  100. {sqlce-0.1.1 → sqlce-0.1.3}/python/sqlce/_sdf_native.pyi +0 -0
  101. {sqlce-0.1.1 → sqlce-0.1.3}/research/catalog_parse/sdf_pages.py +0 -0
  102. {sqlce-0.1.1 → sqlce-0.1.3}/research/catalog_parse/syscatalog.py +0 -0
  103. {sqlce-0.1.1 → sqlce-0.1.3}/research/sdf_d3.py +0 -0
  104. {sqlce-0.1.1 → sqlce-0.1.3}/research/sdf_d4.py +0 -0
  105. {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
@@ -41,6 +41,7 @@ install_manifest.txt
41
41
  compile_commands.json
42
42
  vcpkg_installed/
43
43
  Testing/
44
+ tests/sdf/
44
45
  .cache/
45
46
 
46
47
  .idea/
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
+ ![C++](https://img.shields.io/badge/C%2B%2B-20-blue?logo=cplusplus&logoColor=white)
35
+ ![Python](https://img.shields.io/badge/python-3.8%2B-blue?logo=python&logoColor=white)
36
+ ![Platform](https://img.shields.io/badge/platform-cross--platform-lightgrey)
37
+ ![License](https://img.shields.io/badge/license-MIT-green)
38
+ ![Reverse engineered](https://img.shields.io/badge/Reverse%20engineered%20with-🔬-red)
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
+ ![C++](https://img.shields.io/badge/C%2B%2B-20-blue?logo=cplusplus&logoColor=white)
4
+ ![Python](https://img.shields.io/badge/python-3.8%2B-blue?logo=python&logoColor=white)
5
+ ![Platform](https://img.shields.io/badge/platform-cross--platform-lightgrey)
6
+ ![License](https://img.shields.io/badge/license-MIT-green)
7
+ ![Reverse engineered](https://img.shields.io/badge/Reverse%20engineered%20with-🔬-red)
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 PREFIX "" OUTPUT_NAME "libsdf" POSITION_INDEPENDENT_CODE ON)
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 milliseconds = infrastructure::ReadUInt32LE(chunk, 0);
236
- const std::uint32_t days = infrastructure::ReadUInt32LE(chunk, 4);
237
- return domain::ColumnValue(domain::ColumnValueStorage(domain::DateTimeValue(milliseconds, days)));
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.1
5
- git push origin v0.1.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
+ ![C++](https://img.shields.io/badge/C%2B%2B-20-blue?logo=cplusplus&logoColor=white)
4
+ ![Python](https://img.shields.io/badge/python-3.8%2B-blue?logo=python&logoColor=white)
5
+ ![Platform](https://img.shields.io/badge/platform-cross--platform-lightgrey)
6
+ ![License](https://img.shields.io/badge/license-MIT-green)
7
+ ![Reverse engineered](https://img.shields.io/badge/Reverse%20engineered%20with-🔬-red)
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.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"]
@@ -2,3 +2,6 @@ pybind11>=2.12
2
2
  scikit-build-core>=0.10
3
3
  twine
4
4
  build
5
+ pytest
6
+ pytest-cov
7
+ pythonnet
File without changes