supertable 2.2.5__tar.gz → 2.2.6__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 (179) hide show
  1. {supertable-2.2.5/supertable.egg-info → supertable-2.2.6}/PKG-INFO +1 -1
  2. {supertable-2.2.5 → supertable-2.2.6}/pyproject.toml +1 -1
  3. {supertable-2.2.5 → supertable-2.2.6}/setup.py +1 -1
  4. {supertable-2.2.5 → supertable-2.2.6}/supertable/__init__.py +1 -1
  5. supertable-2.2.6/supertable/engine/engine_config.py +228 -0
  6. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/executor.py +10 -6
  7. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/spark_thrift.py +9 -3
  8. {supertable-2.2.5 → supertable-2.2.6}/supertable/redis_catalog.py +35 -27
  9. {supertable-2.2.5 → supertable-2.2.6}/supertable/redis_keys.py +21 -12
  10. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_redis_key_prefix.py +3 -3
  11. {supertable-2.2.5 → supertable-2.2.6/supertable.egg-info}/PKG-INFO +1 -1
  12. supertable-2.2.5/supertable/engine/engine_config.py +0 -153
  13. {supertable-2.2.5 → supertable-2.2.6}/LICENSE +0 -0
  14. {supertable-2.2.5 → supertable-2.2.6}/README.md +0 -0
  15. {supertable-2.2.5 → supertable-2.2.6}/requirements.txt +0 -0
  16. {supertable-2.2.5 → supertable-2.2.6}/setup.cfg +0 -0
  17. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/__init__.py +0 -0
  18. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/admin.py +0 -0
  19. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/chain.py +0 -0
  20. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/consumers.py +0 -0
  21. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/crypto.py +0 -0
  22. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/events.py +0 -0
  23. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/export.py +0 -0
  24. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/logger.py +0 -0
  25. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/middleware.py +0 -0
  26. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/reader.py +0 -0
  27. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/retention.py +0 -0
  28. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/tests/__init__.py +0 -0
  29. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/tests/test_chain.py +0 -0
  30. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/tests/test_crypto.py +0 -0
  31. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/tests/test_emit.py +0 -0
  32. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/tests/test_events.py +0 -0
  33. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/tests/test_retention.py +0 -0
  34. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/writer_parquet.py +0 -0
  35. {supertable-2.2.5 → supertable-2.2.6}/supertable/audit/writer_redis.py +0 -0
  36. {supertable-2.2.5 → supertable-2.2.6}/supertable/config/__init__.py +0 -0
  37. {supertable-2.2.5 → supertable-2.2.6}/supertable/config/defaults.py +0 -0
  38. {supertable-2.2.5 → supertable-2.2.6}/supertable/config/homedir.py +0 -0
  39. {supertable-2.2.5 → supertable-2.2.6}/supertable/config/settings.py +0 -0
  40. {supertable-2.2.5 → supertable-2.2.6}/supertable/config/tests/__init__.py +0 -0
  41. {supertable-2.2.5 → supertable-2.2.6}/supertable/config/tests/test_defaults.py +0 -0
  42. {supertable-2.2.5 → supertable-2.2.6}/supertable/config/tests/test_homedir.py +0 -0
  43. {supertable-2.2.5 → supertable-2.2.6}/supertable/config/tests/test_settings.py +0 -0
  44. {supertable-2.2.5 → supertable-2.2.6}/supertable/data_classes.py +0 -0
  45. {supertable-2.2.5 → supertable-2.2.6}/supertable/data_reader.py +0 -0
  46. {supertable-2.2.5 → supertable-2.2.6}/supertable/data_writer.py +0 -0
  47. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/__init__.py +0 -0
  48. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/__init__.py +0 -0
  49. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/__main__.py +0 -0
  50. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/check_filter_builder.py +0 -0
  51. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/controller.py +0 -0
  52. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/data_writer_helpers.py +0 -0
  53. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/defaults.py +0 -0
  54. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/dummy_data.py +0 -0
  55. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/read_parquet_header.py +0 -0
  56. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s01_01_01_create_super_table.py +0 -0
  57. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s01_01_02_enable_mirroring_formats.py +0 -0
  58. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s01_02_create_roles.py +0 -0
  59. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s01_03_create_users.py +0 -0
  60. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s02_01_write_dummy_data.py +0 -0
  61. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s02_02_write_single_data.py +0 -0
  62. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s02_03_01_write_staging.py +0 -0
  63. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s02_03_02_create_pipe.py +0 -0
  64. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s02_04_01_write_monitoring_simple.py +0 -0
  65. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s02_04_02_write_monitoring_parallel.py +0 -0
  66. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s02_05_write_tombstone.py +0 -0
  67. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_01_read_data_error.py +0 -0
  68. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_02_01_read_super_data_ok.py +0 -0
  69. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_02_02_read_table_data_ok.py +0 -0
  70. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_03_read_meta.py +0 -0
  71. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_04_read_staging.py +0 -0
  72. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_06_01_read_roles.py +0 -0
  73. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_06_02_read_user.py +0 -0
  74. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_07_01_estimate_read.py +0 -0
  75. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_07_02_estimate_files.py +0 -0
  76. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s03_08_read_snapshot_history.py +0 -0
  77. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s04_01_03_delete_pipe.py +0 -0
  78. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s05_01_delete_table.py +0 -0
  79. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/quickstart/s05_02_delete_super_table.py +0 -0
  80. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/webshop/__init__.py +0 -0
  81. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/webshop/core.py +0 -0
  82. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/webshop/defaults.py +0 -0
  83. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/webshop/generate.py +0 -0
  84. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/webshop/load.py +0 -0
  85. {supertable-2.2.5 → supertable-2.2.6}/supertable/demo/webshop/topup.py +0 -0
  86. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/__init__.py +0 -0
  87. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/data_estimator.py +0 -0
  88. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/duckdb_lite.py +0 -0
  89. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/duckdb_pro.py +0 -0
  90. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/engine_common.py +0 -0
  91. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/engine_enum.py +0 -0
  92. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/plan_stats.py +0 -0
  93. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/tests/__init__.py +0 -0
  94. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/tests/conftest.py +0 -0
  95. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/tests/test_dedup_read.py +0 -0
  96. {supertable-2.2.5 → supertable-2.2.6}/supertable/engine/tests/test_engine.py +0 -0
  97. {supertable-2.2.5 → supertable-2.2.6}/supertable/errors.py +0 -0
  98. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/__init__.py +0 -0
  99. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/benchmarks/__init__.py +0 -0
  100. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/benchmarks/benchmark_locking.py +0 -0
  101. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/benchmarks/measure_lock_speed.py +0 -0
  102. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/benchmarks/measure_lock_time.py +0 -0
  103. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/file_lock.py +0 -0
  104. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/redis_lock.py +0 -0
  105. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/tests/__init__.py +0 -0
  106. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/tests/test_file_lock.py +0 -0
  107. {supertable-2.2.5 → supertable-2.2.6}/supertable/locking/tests/test_redis_lock.py +0 -0
  108. {supertable-2.2.5 → supertable-2.2.6}/supertable/logging.py +0 -0
  109. {supertable-2.2.5 → supertable-2.2.6}/supertable/meta_reader.py +0 -0
  110. {supertable-2.2.5 → supertable-2.2.6}/supertable/mirroring/__init__.py +0 -0
  111. {supertable-2.2.5 → supertable-2.2.6}/supertable/mirroring/mirror_delta.py +0 -0
  112. {supertable-2.2.5 → supertable-2.2.6}/supertable/mirroring/mirror_formats.py +0 -0
  113. {supertable-2.2.5 → supertable-2.2.6}/supertable/mirroring/mirror_iceberg.py +0 -0
  114. {supertable-2.2.5 → supertable-2.2.6}/supertable/mirroring/mirror_parquet.py +0 -0
  115. {supertable-2.2.5 → supertable-2.2.6}/supertable/monitoring/__init__.py +0 -0
  116. {supertable-2.2.5 → supertable-2.2.6}/supertable/monitoring/partitions.py +0 -0
  117. {supertable-2.2.5 → supertable-2.2.6}/supertable/monitoring_writer.py +0 -0
  118. {supertable-2.2.5 → supertable-2.2.6}/supertable/plan_extender.py +0 -0
  119. {supertable-2.2.5 → supertable-2.2.6}/supertable/processing.py +0 -0
  120. {supertable-2.2.5 → supertable-2.2.6}/supertable/query_plan_manager.py +0 -0
  121. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/__init__.py +0 -0
  122. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/access_control.py +0 -0
  123. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/filter_builder.py +0 -0
  124. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/permissions.py +0 -0
  125. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/role_manager.py +0 -0
  126. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/row_column_security.py +0 -0
  127. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/tests/test_filter_builder.py +0 -0
  128. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/tests/test_rbac.py +0 -0
  129. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/tests/test_rbac_per_table.py +0 -0
  130. {supertable-2.2.5 → supertable-2.2.6}/supertable/rbac/user_manager.py +0 -0
  131. {supertable-2.2.5 → supertable-2.2.6}/supertable/redis_connector.py +0 -0
  132. {supertable-2.2.5 → supertable-2.2.6}/supertable/redis_infra.py +0 -0
  133. {supertable-2.2.5 → supertable-2.2.6}/supertable/simple_table.py +0 -0
  134. {supertable-2.2.5 → supertable-2.2.6}/supertable/staging_area.py +0 -0
  135. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/__init__.py +0 -0
  136. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/azure_storage.py +0 -0
  137. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/gcp_storage.py +0 -0
  138. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/local_storage.py +0 -0
  139. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/minio_storage.py +0 -0
  140. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/s3_storage.py +0 -0
  141. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/storage_factory.py +0 -0
  142. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/storage_interface.py +0 -0
  143. {supertable-2.2.5 → supertable-2.2.6}/supertable/storage/tests/test_storage.py +0 -0
  144. {supertable-2.2.5 → supertable-2.2.6}/supertable/super_pipe.py +0 -0
  145. {supertable-2.2.5 → supertable-2.2.6}/supertable/super_table.py +0 -0
  146. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/__init__.py +0 -0
  147. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_align_to_schema_fix.py +0 -0
  148. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_create_if_missing.py +0 -0
  149. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_data_reader.py +0 -0
  150. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_data_reader_preflight.py +0 -0
  151. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_data_writer.py +0 -0
  152. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_data_writer_compact.py +0 -0
  153. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_data_writer_comprehensive.py +0 -0
  154. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_data_writer_tombstones.py +0 -0
  155. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_dedup_on_read_write.py +0 -0
  156. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_errors.py +0 -0
  157. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_meta_reader.py +0 -0
  158. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_monitoring_partitions.py +0 -0
  159. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_monitoring_sink_guard.py +0 -0
  160. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_newer_than.py +0 -0
  161. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_process_delete_only.py +0 -0
  162. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_processing.py +0 -0
  163. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_processing_compact_resources.py +0 -0
  164. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_query_sql.py +0 -0
  165. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_simple_table.py +0 -0
  166. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_small_file_compaction.py +0 -0
  167. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_super_table.py +0 -0
  168. {supertable-2.2.5 → supertable-2.2.6}/supertable/tests/test_supertable_all.py +0 -0
  169. {supertable-2.2.5 → supertable-2.2.6}/supertable/utils/__init__.py +0 -0
  170. {supertable-2.2.5 → supertable-2.2.6}/supertable/utils/helper.py +0 -0
  171. {supertable-2.2.5 → supertable-2.2.6}/supertable/utils/profiler.py +0 -0
  172. {supertable-2.2.5 → supertable-2.2.6}/supertable/utils/sql_parser.py +0 -0
  173. {supertable-2.2.5 → supertable-2.2.6}/supertable/utils/tests/test_sql_parser_columns.py +0 -0
  174. {supertable-2.2.5 → supertable-2.2.6}/supertable/utils/timer.py +0 -0
  175. {supertable-2.2.5 → supertable-2.2.6}/supertable.egg-info/SOURCES.txt +0 -0
  176. {supertable-2.2.5 → supertable-2.2.6}/supertable.egg-info/dependency_links.txt +0 -0
  177. {supertable-2.2.5 → supertable-2.2.6}/supertable.egg-info/entry_points.txt +0 -0
  178. {supertable-2.2.5 → supertable-2.2.6}/supertable.egg-info/requires.txt +0 -0
  179. {supertable-2.2.5 → supertable-2.2.6}/supertable.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supertable
3
- Version: 2.2.5
3
+ Version: 2.2.6
4
4
  Summary: SuperTable — versioned data lake library for SQL analytics on Parquet + Redis.
5
5
  Author: Levente Kupas
6
6
  Author-email: Levente Kupas <lkupas@kladnasoft.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "supertable"
7
- version = "2.2.5"
7
+ version = "2.2.6"
8
8
  description = "SuperTable — versioned data lake library for SQL analytics on Parquet + Redis."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -19,7 +19,7 @@ long_description = readme.read_text(encoding="utf-8") if readme.exists() else ""
19
19
 
20
20
  setup(
21
21
  name="supertable",
22
- version="2.2.5",
22
+ version="2.2.6",
23
23
  description="SuperTable — versioned data lake library for SQL analytics on Parquet + Redis.",
24
24
  long_description=long_description,
25
25
  long_description_content_type="text/markdown",
@@ -25,7 +25,7 @@ See the ``supertable.demo`` package for runnable end-to-end demos and the
25
25
  project documentation for the full API surface.
26
26
  """
27
27
 
28
- __version__ = "2.2.5"
28
+ __version__ = "2.2.6"
29
29
 
30
30
  # Re-export the core public surface so users can do ``from supertable import …``
31
31
  # instead of remembering submodule paths.
@@ -0,0 +1,228 @@
1
+ # route: supertable.engine.engine_config
2
+ """
3
+ Engine runtime configuration — single source of truth for resolution.
4
+
5
+ Engine config is stored once per organization at **system scope**
6
+ (``supertable:{org}:system:engine:duckdb``). It is global to the org, not
7
+ per-supertable, and is resolved **live on every query** (no caching) so that
8
+ changes made in the UI take effect on the next query without a restart or a
9
+ connection reset.
10
+
11
+ Two kinds of settings live in that document:
12
+
13
+ * **Shared auto-pick thresholds** (one value, used by the router):
14
+ ``engine_lite_max_bytes``, ``engine_spark_min_bytes``,
15
+ ``engine_freshness_sec``. These describe the *boundaries* between engines
16
+ so they cannot be per-engine.
17
+ * **Per-engine DuckDB runtime pragmas** (separate values for Lite and Pro):
18
+ ``duckdb_memory_limit``, ``duckdb_io_multiplier``, ``duckdb_threads``,
19
+ ``duckdb_http_timeout``, ``duckdb_external_cache_size``. Each DuckDB engine
20
+ carries its own copy under a ``"lite"`` / ``"pro"`` section, so tuning Lite
21
+ never changes Pro and vice-versa.
22
+
23
+ Stored document shape::
24
+
25
+ {
26
+ "engine_lite_max_bytes": "104857600",
27
+ "engine_spark_min_bytes": "10737418240",
28
+ "engine_freshness_sec": "300",
29
+ "lite": {"duckdb_memory_limit": "1GB", ...},
30
+ "pro": {"duckdb_memory_limit": "8GB", ...},
31
+ "modified_ms": 1750000000000
32
+ }
33
+
34
+ Resolution precedence, per field::
35
+
36
+ Redis (org system config) → environment variable → built-in default
37
+
38
+ The environment variable is a single global fallback shared by both engines;
39
+ per-engine differentiation therefore only happens once a value is stored in
40
+ Redis (via the UI).
41
+
42
+ Entry points (all share that precedence so engine and UI never disagree):
43
+
44
+ * :func:`resolve_engine_configs` → ``{"lite": cfg, "pro": cfg}`` in a
45
+ single Redis read; used by the
46
+ executor.
47
+ * :func:`resolve_engine_config` → typed :class:`EngineRuntimeConfig`
48
+ for one engine (convenience).
49
+ * :func:`resolve_engine_config_provenance` → ``{shared, lite, pro,
50
+ modified_ms}`` per-field
51
+ ``{value, source, env_var,
52
+ default}`` for the API / UI.
53
+
54
+ Built-in defaults mirror the :mod:`supertable.config.settings` dataclass
55
+ defaults, so behaviour is identical whether a value flows through ``settings``
56
+ (no Redis override) or through this resolver.
57
+ """
58
+ from __future__ import annotations
59
+
60
+ import os
61
+ from dataclasses import dataclass
62
+ from typing import Any, Dict, Optional, Tuple
63
+
64
+
65
+ # Shared auto-pick thresholds: key → (env var, built-in default as string).
66
+ _SHARED_SPEC: Dict[str, Tuple[str, str]] = {
67
+ "engine_lite_max_bytes": ("SUPERTABLE_ENGINE_LITE_MAX_BYTES", str(100 * 1024 * 1024)),
68
+ "engine_spark_min_bytes": ("SUPERTABLE_ENGINE_SPARK_MIN_BYTES", str(10 * 1024 * 1024 * 1024)),
69
+ "engine_freshness_sec": ("SUPERTABLE_ENGINE_FRESHNESS_SEC", "300"),
70
+ }
71
+
72
+ # Per-engine DuckDB runtime pragmas: key → (env var, built-in default as string).
73
+ # Defaults mirror the settings dataclass defaults in supertable/config/settings.py.
74
+ _DUCKDB_SPEC: Dict[str, Tuple[str, str]] = {
75
+ "duckdb_memory_limit": ("SUPERTABLE_DUCKDB_MEMORY_LIMIT", "1GB"),
76
+ "duckdb_io_multiplier": ("SUPERTABLE_DUCKDB_IO_MULTIPLIER", "3"),
77
+ "duckdb_threads": ("SUPERTABLE_DUCKDB_THREADS", ""),
78
+ "duckdb_http_timeout": ("SUPERTABLE_DUCKDB_HTTP_TIMEOUT", ""),
79
+ "duckdb_external_cache_size": ("SUPERTABLE_DUCKDB_EXTERNAL_CACHE_SIZE", "5GB"),
80
+ }
81
+
82
+ # The two DuckDB engines that each carry their own runtime section.
83
+ DUCKDB_ENGINES: Tuple[str, ...] = ("lite", "pro")
84
+
85
+ # Public whitelists (for callers / catalog / tests).
86
+ SHARED_CONFIG_FIELDS: Tuple[str, ...] = tuple(_SHARED_SPEC.keys())
87
+ DUCKDB_CONFIG_FIELDS: Tuple[str, ...] = tuple(_DUCKDB_SPEC.keys())
88
+ ENGINE_CONFIG_FIELDS: Tuple[str, ...] = SHARED_CONFIG_FIELDS + DUCKDB_CONFIG_FIELDS
89
+
90
+
91
+ @dataclass(frozen=True)
92
+ class EngineRuntimeConfig:
93
+ """Effective, typed config for one DuckDB engine resolution.
94
+
95
+ Carries the shared auto-pick thresholds (identical across engines) plus the
96
+ DuckDB session pragmas for the engine it was resolved for.
97
+ """
98
+
99
+ # auto-pick thresholds (shared)
100
+ engine_lite_max_bytes: int
101
+ engine_spark_min_bytes: int
102
+ engine_freshness_sec: int
103
+
104
+ # duckdb session pragmas (per-engine, re-applied live per query)
105
+ duckdb_memory_limit: str # e.g. "1GB"
106
+ duckdb_io_multiplier: float
107
+ duckdb_threads: Optional[int] # None → auto-derive from memory limit
108
+ duckdb_http_timeout: Optional[int] # seconds; None → leave DuckDB default
109
+ duckdb_external_cache_size: str # "" → external file cache disabled
110
+
111
+
112
+ def _redis_cfg(org: str, catalog: Optional[Any]) -> Dict[str, Any]:
113
+ """Read the stored engine config for ``org`` (best effort).
114
+
115
+ Returns an empty dict when no catalog is supplied or Redis is unreachable,
116
+ so the resolver degrades cleanly to env vars + defaults.
117
+ """
118
+ if not (org and catalog is not None):
119
+ return {}
120
+ try:
121
+ return catalog.get_engine_config(org) or {}
122
+ except Exception:
123
+ return {}
124
+
125
+
126
+ def _effective(stored: Dict[str, Any], key: str, spec: Dict[str, Tuple[str, str]]) -> Tuple[str, str]:
127
+ """Return ``(value, source)`` for one field. source ∈ redis|env|default.
128
+
129
+ ``stored`` is the dict the value is looked up in directly: the top-level
130
+ document for shared fields, or the ``lite`` / ``pro`` sub-section for
131
+ per-engine fields.
132
+ """
133
+ env_var, default = spec[key]
134
+ redis_val = stored.get(key) if isinstance(stored, dict) else None
135
+ if redis_val is not None and str(redis_val).strip() != "":
136
+ return str(redis_val).strip(), "redis"
137
+ env_val = (os.getenv(env_var) or "").strip()
138
+ if env_val:
139
+ return env_val, "env"
140
+ return default, "default"
141
+
142
+
143
+ def _to_int(s: Any, fallback: int) -> int:
144
+ try:
145
+ return int(str(s).strip())
146
+ except (TypeError, ValueError):
147
+ return fallback
148
+
149
+
150
+ def _to_float(s: Any, fallback: float) -> float:
151
+ try:
152
+ return float(str(s).strip())
153
+ except (TypeError, ValueError):
154
+ return fallback
155
+
156
+
157
+ def _build_runtime(redis_cfg: Dict[str, Any], engine: str) -> EngineRuntimeConfig:
158
+ """Build the typed config for ``engine`` ("lite"/"pro") from a stored doc."""
159
+ section = redis_cfg.get(engine) if isinstance(redis_cfg.get(engine), dict) else {}
160
+
161
+ sv = {k: _effective(redis_cfg, k, _SHARED_SPEC)[0] for k in _SHARED_SPEC}
162
+ dv = {k: _effective(section, k, _DUCKDB_SPEC)[0] for k in _DUCKDB_SPEC}
163
+
164
+ threads = _to_int(dv["duckdb_threads"], 0) if dv["duckdb_threads"] else 0
165
+ http = _to_int(dv["duckdb_http_timeout"], 0) if dv["duckdb_http_timeout"] else 0
166
+
167
+ return EngineRuntimeConfig(
168
+ engine_lite_max_bytes=_to_int(sv["engine_lite_max_bytes"], 100 * 1024 * 1024),
169
+ engine_spark_min_bytes=_to_int(sv["engine_spark_min_bytes"], 10 * 1024 * 1024 * 1024),
170
+ engine_freshness_sec=_to_int(sv["engine_freshness_sec"], 300),
171
+ duckdb_memory_limit=dv["duckdb_memory_limit"].strip() or "1GB",
172
+ duckdb_io_multiplier=_to_float(dv["duckdb_io_multiplier"], 3.0),
173
+ duckdb_threads=(threads if threads > 0 else None),
174
+ duckdb_http_timeout=(http if http > 0 else None),
175
+ duckdb_external_cache_size=dv["duckdb_external_cache_size"].strip(),
176
+ )
177
+
178
+
179
+ def resolve_engine_configs(org: str, catalog: Optional[Any] = None) -> Dict[str, EngineRuntimeConfig]:
180
+ """Resolve both DuckDB engines in a single Redis read.
181
+
182
+ Returns ``{"lite": EngineRuntimeConfig, "pro": EngineRuntimeConfig}``. The
183
+ shared thresholds are identical in both; only the DuckDB pragmas differ.
184
+ """
185
+ cfg = _redis_cfg(org, catalog)
186
+ return {e: _build_runtime(cfg, e) for e in DUCKDB_ENGINES}
187
+
188
+
189
+ def resolve_engine_config(org: str, catalog: Optional[Any] = None, engine: str = "lite") -> EngineRuntimeConfig:
190
+ """Resolve effective config for a single ``engine`` (Redis → env → default).
191
+
192
+ ``catalog`` is an optional ``RedisCatalog``. When it is None or Redis is
193
+ unreachable the resolver falls back to environment variables and built-in
194
+ defaults, which keeps the engine usable without Redis (e.g. in unit tests).
195
+ """
196
+ engine = engine if engine in DUCKDB_ENGINES else "lite"
197
+ return _build_runtime(_redis_cfg(org, catalog), engine)
198
+
199
+
200
+ def resolve_engine_config_provenance(org: str, catalog: Optional[Any] = None) -> Dict[str, Any]:
201
+ """Per-field provenance for the UI.
202
+
203
+ Shape::
204
+
205
+ {
206
+ "shared": {field: {value, source, env_var, default}, ...},
207
+ "lite": {field: {value, source, env_var, default}, ...},
208
+ "pro": {field: {value, source, env_var, default}, ...},
209
+ "modified_ms": <int|None>,
210
+ }
211
+
212
+ ``source`` is one of ``redis`` / ``env`` / ``default``.
213
+ """
214
+ cfg = _redis_cfg(org, catalog)
215
+
216
+ def _block(stored: Dict[str, Any], spec: Dict[str, Tuple[str, str]]) -> Dict[str, Any]:
217
+ out: Dict[str, Any] = {}
218
+ for key, (env_var, default) in spec.items():
219
+ value, source = _effective(stored, key, spec)
220
+ out[key] = {"value": value, "source": source, "env_var": env_var, "default": default}
221
+ return out
222
+
223
+ result: Dict[str, Any] = {"shared": _block(cfg, _SHARED_SPEC)}
224
+ for engine in DUCKDB_ENGINES:
225
+ section = cfg.get(engine) if isinstance(cfg.get(engine), dict) else {}
226
+ result[engine] = _block(section, _DUCKDB_SPEC)
227
+ result["modified_ms"] = cfg.get("modified_ms")
228
+ return result
@@ -16,7 +16,7 @@ from supertable.utils.sql_parser import SQLParser
16
16
  from supertable.engine.engine_enum import Engine
17
17
  from supertable.engine.duckdb_lite import DuckDBLite
18
18
  from supertable.engine.duckdb_pro import DuckDBPro
19
- from supertable.engine.engine_config import resolve_engine_config, EngineRuntimeConfig
19
+ from supertable.engine.engine_config import resolve_engine_configs, EngineRuntimeConfig
20
20
  from supertable.data_classes import Reflection
21
21
  from supertable.config.defaults import logger
22
22
 
@@ -143,10 +143,14 @@ class Executor:
143
143
  log_prefix: str,
144
144
  ) -> Tuple[pd.DataFrame, str]:
145
145
  # Resolve engine config live (Redis → env → default) for this query so
146
- # UI changes take effect immediately without restart or cache.
147
- cfg = resolve_engine_config(self.organization, self._get_catalog())
146
+ # UI changes take effect immediately without restart or cache. Lite and
147
+ # Pro carry independent DuckDB pragmas; the shared auto-pick thresholds
148
+ # are identical in both, so either may drive the routing decision.
149
+ cfgs = resolve_engine_configs(self.organization, self._get_catalog())
150
+ lite_cfg = cfgs["lite"]
151
+ pro_cfg = cfgs["pro"]
148
152
 
149
- chosen = engine if engine != Engine.AUTO else self._auto_pick(reflection, cfg)
153
+ chosen = engine if engine != Engine.AUTO else self._auto_pick(reflection, lite_cfg)
150
154
 
151
155
  def timer_capture(evt: str):
152
156
  timer.capture_and_reset_timing(evt)
@@ -158,7 +162,7 @@ class Executor:
158
162
  query_manager=query_manager,
159
163
  timer_capture=timer_capture,
160
164
  log_prefix=log_prefix,
161
- engine_config=cfg,
165
+ engine_config=lite_cfg,
162
166
  )
163
167
  used = "duckdb_lite"
164
168
 
@@ -170,7 +174,7 @@ class Executor:
170
174
  query_manager=query_manager,
171
175
  timer_capture=timer_capture,
172
176
  log_prefix=log_prefix,
173
- engine_config=cfg,
177
+ engine_config=pro_cfg,
174
178
  )
175
179
  used = "duckdb_pro"
176
180
 
@@ -25,7 +25,7 @@ from supertable.engine.engine_common import (
25
25
  escape_parquet_path,
26
26
  )
27
27
 
28
- from urllib.parse import urlparse
28
+ from urllib.parse import urlparse, unquote
29
29
 
30
30
  # Suppress verbose INFO logging from PyHive and Thrift libraries.
31
31
  # PyHive logs every SQL statement (CREATE VIEW, DROP VIEW, SET, etc.)
@@ -77,8 +77,14 @@ def _to_s3a_path(file_path: str) -> str:
77
77
 
78
78
  if file_path.startswith("http://") or file_path.startswith("https://"):
79
79
  parsed = urlparse(file_path)
80
- # path is /bucket/key — strip the leading slash, split into bucket + key
81
- path = parsed.path.lstrip("/")
80
+ # path is /bucket/key — strip the leading slash, split into bucket + key.
81
+ # urlparse leaves percent-escapes intact, and boto3 presigning encodes
82
+ # the key (Hive partition dirs like ``year=2006`` become ``year%3D2006``).
83
+ # Spark's S3A client expects the *raw* object key and re-encodes itself,
84
+ # so we must decode here — otherwise Ceph RGW, which matches keys
85
+ # literally, 404s on the ``%3D`` form. The presign query string is
86
+ # dropped on purpose: Spark authenticates with its own fs.s3a.* creds.
87
+ path = unquote(parsed.path).lstrip("/")
82
88
  if "/" in path:
83
89
  return f"s3a://{path}"
84
90
  # Degenerate case: only bucket, no key — return as-is
@@ -1468,7 +1468,7 @@ return 1
1468
1468
  return 0
1469
1469
 
1470
1470
  # ========================================================================= #
1471
- # Spark Thrift cluster management (org-scoped: supertable:{org}:system:spark:thrifts)
1471
+ # Spark Thrift cluster management (org-scoped: supertable:{org}:system:engine:thrifts)
1472
1472
  # ========================================================================= #
1473
1473
 
1474
1474
  def register_spark_cluster(self, org: str, cluster_id: str, config: Dict[str, Any]) -> None:
@@ -1497,7 +1497,7 @@ return 1
1497
1497
  doc.setdefault("s3_enabled", True)
1498
1498
  doc["modified_ms"] = _now_ms()
1499
1499
  self.r.hset(
1500
- RK.spark_thrifts(org),
1500
+ RK.engine_thrifts(org),
1501
1501
  cluster_id,
1502
1502
  json.dumps(doc, default=str),
1503
1503
  )
@@ -1512,7 +1512,7 @@ return 1
1512
1512
  if not org:
1513
1513
  return []
1514
1514
  try:
1515
- raw = self.r.hgetall(RK.spark_thrifts(org))
1515
+ raw = self.r.hgetall(RK.engine_thrifts(org))
1516
1516
  clusters = []
1517
1517
  for _cid, data in raw.items():
1518
1518
  try:
@@ -1530,7 +1530,7 @@ return 1
1530
1530
  if not (org and cluster_id):
1531
1531
  return False
1532
1532
  try:
1533
- removed = self.r.hdel(RK.spark_thrifts(org), cluster_id)
1533
+ removed = self.r.hdel(RK.engine_thrifts(org), cluster_id)
1534
1534
  if removed:
1535
1535
  logger.info(f"[redis-catalog] spark thrift deleted: {org}/{cluster_id}")
1536
1536
  return bool(removed)
@@ -1578,7 +1578,7 @@ return 1
1578
1578
  return candidates[0]
1579
1579
 
1580
1580
  # ========================================================================= #
1581
- # Spark Plug management (org-scoped: supertable:{org}:system:spark:plugs)
1581
+ # Spark Plug management (org-scoped: supertable:{org}:system:engine:plugs)
1582
1582
  # ========================================================================= #
1583
1583
 
1584
1584
  def register_spark_plug(self, org: str, plug_id: str, config: Dict[str, Any]) -> None:
@@ -1604,7 +1604,7 @@ return 1
1604
1604
  doc.setdefault("webui_url", "")
1605
1605
  doc["modified_ms"] = _now_ms()
1606
1606
  self.r.hset(
1607
- RK.spark_plugs(org),
1607
+ RK.engine_plugs(org),
1608
1608
  plug_id,
1609
1609
  json.dumps(doc, default=str),
1610
1610
  )
@@ -1669,45 +1669,53 @@ return 1
1669
1669
  # Canonical field names and their env-var counterparts. Used by the
1670
1670
  # resolver in engine_common to fall back to os.getenv when a field
1671
1671
  # is absent from Redis.
1672
- ENGINE_CONFIG_FIELDS = {
1673
- "engine_lite_max_bytes": "SUPERTABLE_ENGINE_LITE_MAX_BYTES",
1674
- "engine_spark_min_bytes": "SUPERTABLE_ENGINE_SPARK_MIN_BYTES",
1675
- "engine_freshness_sec": "SUPERTABLE_ENGINE_FRESHNESS_SEC",
1676
- "duckdb_memory_limit": "SUPERTABLE_DUCKDB_MEMORY_LIMIT",
1677
- "duckdb_io_multiplier": "SUPERTABLE_DUCKDB_IO_MULTIPLIER",
1678
- "duckdb_threads": "SUPERTABLE_DUCKDB_THREADS",
1679
- "duckdb_http_timeout": "SUPERTABLE_DUCKDB_HTTP_TIMEOUT",
1680
- "duckdb_external_cache_size": "SUPERTABLE_DUCKDB_EXTERNAL_CACHE_SIZE",
1681
- }
1672
+ # Per-engine DuckDB runtime pragmas (separate sections for "lite" / "pro").
1673
+ DUCKDB_CONFIG_FIELDS = (
1674
+ "duckdb_memory_limit",
1675
+ "duckdb_io_multiplier",
1676
+ "duckdb_threads",
1677
+ "duckdb_http_timeout",
1678
+ "duckdb_external_cache_size",
1679
+ )
1680
+ DUCKDB_ENGINES = ("lite", "pro")
1682
1681
 
1683
1682
  def set_engine_config(
1684
1683
  self,
1685
1684
  org: str,
1685
+ engine: str,
1686
1686
  config: Dict[str, Any],
1687
1687
  ) -> bool:
1688
- """Store engine runtime configuration (DuckDB + auto-pick thresholds).
1688
+ """Store DuckDB runtime configuration for one engine ("lite" / "pro").
1689
1689
 
1690
- Org-level system scope: one engine config per organization, applied
1690
+ Org-level system scope: one engine document per organization, applied
1691
1691
  globally across all supertables (not per-supertable).
1692
1692
 
1693
- The config dict is stored as a JSON string. Only recognised fields
1694
- (see ENGINE_CONFIG_FIELDS) are persisted — unknown keys are silently
1695
- dropped to prevent injection of arbitrary settings.
1693
+ Only the requested engine's section is replaced; the other engine's
1694
+ section and the shared auto-pick thresholds are preserved. Within the
1695
+ section it is a full replace (last-write-wins) — omitting a field clears
1696
+ its Redis override so it falls back to env / default at read time.
1696
1697
 
1697
- Existing config is fully replaced (last-write-wins).
1698
+ Only recognised DuckDB fields (see DUCKDB_CONFIG_FIELDS) are persisted —
1699
+ unknown keys are silently dropped to prevent injection of arbitrary
1700
+ settings.
1698
1701
  """
1699
1702
  if not org:
1700
1703
  return False
1704
+ engine = (engine or "").strip().lower()
1705
+ if engine not in self.DUCKDB_ENGINES:
1706
+ return False
1701
1707
  try:
1702
- # Whitelist recognised fields only.
1703
- doc = {
1708
+ doc = self.get_engine_config(org) or {}
1709
+ # Whitelist recognised DuckDB fields only for this engine section.
1710
+ section = {
1704
1711
  k: config[k]
1705
- for k in self.ENGINE_CONFIG_FIELDS
1712
+ for k in self.DUCKDB_CONFIG_FIELDS
1706
1713
  if k in config and config[k] is not None and str(config[k]).strip() != ""
1707
1714
  }
1715
+ doc[engine] = section
1708
1716
  doc["modified_ms"] = _now_ms()
1709
1717
  self.r.set(
1710
- RK.config_engine(org),
1718
+ RK.engine_duckdb(org),
1711
1719
  json.dumps(doc, default=str),
1712
1720
  )
1713
1721
  return True
@@ -1726,7 +1734,7 @@ return 1
1726
1734
  if not org:
1727
1735
  return None
1728
1736
  try:
1729
- raw = self.r.get(RK.config_engine(org))
1737
+ raw = self.r.get(RK.engine_duckdb(org))
1730
1738
  if raw:
1731
1739
  return json.loads(raw)
1732
1740
  except (redis.RedisError, json.JSONDecodeError) as e:
@@ -35,10 +35,10 @@ Hierarchy (v2)
35
35
  shares:
36
36
  doc:{share_id} STRING share definition
37
37
  index SET share IDs
38
- spark:
38
+ engine:
39
39
  thrifts HASH Spark Thrift clusters
40
40
  plugs HASH Spark Plug runtimes
41
- config:engine STRING engine runtime config
41
+ duckdb STRING DuckDB runtime config
42
42
  lakes: ← user-data sentinel
43
43
  {sup}: ← supertable scope
44
44
  meta:
@@ -121,7 +121,7 @@ SUPERTABLE_PREFIX: str = "supertable"
121
121
  DATAISLAND_PREFIX: str = "dataisland"
122
122
 
123
123
  # Position-2 sentinel under ``supertable:{org}:`` for org-level platform
124
- # state (auth tokens, audit, shares, spark).
124
+ # state (auth tokens, audit, shares, engine).
125
125
  SYSTEM_SCOPE: str = "system"
126
126
 
127
127
  # Position-2 sentinel under ``supertable:{org}:`` for user-supplied
@@ -394,16 +394,21 @@ def share_index(org: str) -> str:
394
394
  return f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{SYSTEM_SCOPE}:shares:index"
395
395
 
396
396
 
397
- # --- Org-level Spark ------------------------------------------------------- #
397
+ # --- Org-level Engine ------------------------------------------------------ #
398
+ #
399
+ # All query-engine state lives under one ``system:engine:`` namespace: the
400
+ # Spark Thrift cluster registry, the Spark Plug runtime registry, and the
401
+ # DuckDB (Lite/Pro) runtime config document. Grouping them keeps every engine
402
+ # knob the UI exposes under a single, discoverable path.
398
403
 
399
- def spark_thrifts(org: str) -> str:
404
+ def engine_thrifts(org: str) -> str:
400
405
  """Spark Thrift cluster registry (HASH)."""
401
- return f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{SYSTEM_SCOPE}:spark:thrifts"
406
+ return f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{SYSTEM_SCOPE}:engine:thrifts"
402
407
 
403
408
 
404
- def spark_plugs(org: str) -> str:
409
+ def engine_plugs(org: str) -> str:
405
410
  """Spark Plug runtime registry (HASH)."""
406
- return f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{SYSTEM_SCOPE}:spark:plugs"
411
+ return f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{SYSTEM_SCOPE}:engine:plugs"
407
412
 
408
413
 
409
414
  # =========================================================================== #
@@ -570,11 +575,15 @@ def pipe_pattern(org: str, sup: str, staging_name: str) -> str:
570
575
  )
571
576
 
572
577
 
573
- # --- Engine config --------------------------------------------------------- #
578
+ # --- DuckDB engine config -------------------------------------------------- #
579
+
580
+ def engine_duckdb(org: str) -> str:
581
+ """DuckDB (Lite/Pro) runtime config (STRING). Org-level system scope (global, not per-supertable).
574
582
 
575
- def config_engine(org: str) -> str:
576
- """Engine runtime config (STRING). Org-level system scope (global, not per-supertable)."""
577
- return f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{SYSTEM_SCOPE}:config:engine"
583
+ One document holds both engines' DuckDB pragmas plus the shared auto-pick
584
+ thresholds; see :mod:`supertable.engine.engine_config`.
585
+ """
586
+ return f"{SUPERTABLE_PREFIX}:{_safe('org', org)}:{SYSTEM_SCOPE}:engine:duckdb"
578
587
 
579
588
 
580
589
  # --- Locks ----------------------------------------------------------------- #
@@ -68,9 +68,9 @@ def _all_helpers() -> list[tuple[str, str, str]]:
68
68
  ("audit_legal_hold", RK.audit_legal_hold(ORG), f"{sys_pre}:audit:legal_hold"),
69
69
  ("share_doc", RK.share_doc(ORG, SHARE), f"{sys_pre}:shares:doc:{SHARE}"),
70
70
  ("share_index", RK.share_index(ORG), f"{sys_pre}:shares:index"),
71
- ("spark_thrifts", RK.spark_thrifts(ORG), f"{sys_pre}:spark:thrifts"),
72
- ("spark_plugs", RK.spark_plugs(ORG), f"{sys_pre}:spark:plugs"),
73
- ("config_engine", RK.config_engine(ORG), f"{sys_pre}:config:engine"),
71
+ ("engine_thrifts", RK.engine_thrifts(ORG), f"{sys_pre}:engine:thrifts"),
72
+ ("engine_plugs", RK.engine_plugs(ORG), f"{sys_pre}:engine:plugs"),
73
+ ("engine_duckdb", RK.engine_duckdb(ORG), f"{sys_pre}:engine:duckdb"),
74
74
 
75
75
  # ---- Lakes scope (per-org supertable enumeration) ---------------
76
76
  ("lakes_scope", RK.lakes_scope(ORG), f"supertable:{ORG}:lakes"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supertable
3
- Version: 2.2.5
3
+ Version: 2.2.6
4
4
  Summary: SuperTable — versioned data lake library for SQL analytics on Parquet + Redis.
5
5
  Author: Levente Kupas
6
6
  Author-email: Levente Kupas <lkupas@kladnasoft.com>