rubydb 0.1.0

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 (436) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +20 -0
  3. data/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  4. data/.github/ISSUE_TEMPLATE/feature.yml +0 -0
  5. data/.github/ISSUE_TEMPLATE/performance.yml +0 -0
  6. data/.github/ISSUE_TEMPLATE/security.yml +0 -0
  7. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  8. data/.github/dependabot.yml +0 -0
  9. data/.github/workflows/benchmark.yml +26 -0
  10. data/.github/workflows/compatibility.yml +63 -0
  11. data/.github/workflows/fuzz.yml +33 -0
  12. data/.github/workflows/lint.yml +21 -0
  13. data/.github/workflows/operations.yml +24 -0
  14. data/.github/workflows/production-validation.yml +111 -0
  15. data/.github/workflows/release.yml +77 -0
  16. data/.github/workflows/security.yml +37 -0
  17. data/.github/workflows/test.yml +26 -0
  18. data/.github/workflows/workload.yml +58 -0
  19. data/.gitignore +43 -0
  20. data/.rspec +4 -0
  21. data/.rubocop.yml +44 -0
  22. data/.ruby-version +1 -0
  23. data/.standard.yml +14 -0
  24. data/ARCHITECTURE.md +21 -0
  25. data/CHANGELOG.md +31 -0
  26. data/CODE_OF_CONDUCT.md +13 -0
  27. data/CONTRIBUTING.md +29 -0
  28. data/GOVERNANCE.md +16 -0
  29. data/Gemfile +17 -0
  30. data/Gemfile.lock +71 -0
  31. data/LICENSE +60 -0
  32. data/README.md +162 -0
  33. data/ROADMAP.md +27 -0
  34. data/Rakefile +71 -0
  35. data/SECURITY.md +54 -0
  36. data/SUPPORT.md +14 -0
  37. data/adapters/activerecord/Gemfile +11 -0
  38. data/adapters/activerecord/README.md +9 -0
  39. data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +887 -0
  40. data/adapters/activerecord/rubydb-activerecord.gemspec +27 -0
  41. data/adapters/activerecord/spec/rubydb_adapter_integration_spec.rb +151 -0
  42. data/adapters/ruby/README.md +18 -0
  43. data/adapters/sequel/README.md +11 -0
  44. data/bin/rubydb +19 -0
  45. data/bin/rubydb-server +126 -0
  46. data/config/development.yml +0 -0
  47. data/config/monitoring/prometheus-alerts.yml +39 -0
  48. data/config/production.yml +36 -0
  49. data/config/test.yml +0 -0
  50. data/docs/README.md +71 -0
  51. data/docs/architecture/concurrency.md +14 -0
  52. data/docs/architecture/current-state.md +125 -0
  53. data/docs/architecture/execution-engine.md +25 -0
  54. data/docs/architecture/indexes.md +19 -0
  55. data/docs/architecture/mvcc.md +19 -0
  56. data/docs/architecture/overview.md +13 -0
  57. data/docs/architecture/pages.md +11 -0
  58. data/docs/architecture/production-roadmap.md +82 -0
  59. data/docs/architecture/query-planner.md +20 -0
  60. data/docs/architecture/recovery.md +18 -0
  61. data/docs/architecture/sql-engine.md +12 -0
  62. data/docs/architecture/storage-engine.md +14 -0
  63. data/docs/architecture/transactions.md +10 -0
  64. data/docs/architecture/wal.md +28 -0
  65. data/docs/cli-cheatsheet.md +98 -0
  66. data/docs/cli.md +275 -0
  67. data/docs/contributing/architecture.md +9 -0
  68. data/docs/contributing/benchmarking.md +14 -0
  69. data/docs/contributing/development.md +16 -0
  70. data/docs/contributing/release-process.md +49 -0
  71. data/docs/contributing/testing.md +16 -0
  72. data/docs/debugging.md +229 -0
  73. data/docs/developer/branching.md +10 -0
  74. data/docs/developer/database-diff.md +10 -0
  75. data/docs/developer/local-development.md +17 -0
  76. data/docs/developer/snapshots.md +9 -0
  77. data/docs/developer/temporal-data.md +10 -0
  78. data/docs/developer-guide.md +297 -0
  79. data/docs/getting-started/first-database.md +16 -0
  80. data/docs/getting-started/first-query.md +13 -0
  81. data/docs/getting-started/installation.md +19 -0
  82. data/docs/getting-started/local-to-production.md +300 -0
  83. data/docs/getting-started/quickstart.md +17 -0
  84. data/docs/getting-started/rails.md +16 -0
  85. data/docs/hardening_backlog.md +93 -0
  86. data/docs/lessons-learned.md +112 -0
  87. data/docs/operations/backups.md +33 -0
  88. data/docs/operations/disaster-recovery.md +31 -0
  89. data/docs/operations/failover.md +30 -0
  90. data/docs/operations/monitoring.md +25 -0
  91. data/docs/operations/production-guide.md +295 -0
  92. data/docs/operations/production-runbook.md +45 -0
  93. data/docs/operations/replication.md +33 -0
  94. data/docs/operations/restore.md +6 -0
  95. data/docs/operations/runbook.md +34 -0
  96. data/docs/operations/upgrades.md +14 -0
  97. data/docs/operations/workload-testing.md +17 -0
  98. data/docs/production-readiness.md +118 -0
  99. data/docs/production_validation.md +150 -0
  100. data/docs/rails/active-record.md +11 -0
  101. data/docs/rails/compatibility-guide.md +90 -0
  102. data/docs/rails/database-yml.md +92 -0
  103. data/docs/rails/installation.md +17 -0
  104. data/docs/rails/migrations.md +17 -0
  105. data/docs/rails/production.md +82 -0
  106. data/docs/rails/troubleshooting.md +18 -0
  107. data/docs/release.md +59 -0
  108. data/docs/server/architecture.md +10 -0
  109. data/docs/server/authentication.md +10 -0
  110. data/docs/server/configuration.md +16 -0
  111. data/docs/server/connection-pooling.md +10 -0
  112. data/docs/server/deployment.md +10 -0
  113. data/docs/server/protocol.md +12 -0
  114. data/docs/sql/compatibility-guide.md +82 -0
  115. data/docs/sql/compatibility.md +39 -0
  116. data/docs/sql/data-types.md +10 -0
  117. data/docs/sql/functions.md +9 -0
  118. data/docs/sql/joins.md +9 -0
  119. data/docs/sql/operators.md +9 -0
  120. data/docs/sql/sqlite-compatibility.md +21 -0
  121. data/docs/sql/syntax.md +10 -0
  122. data/docs/sql/transactions.md +10 -0
  123. data/docs/troubleshooting.md +244 -0
  124. data/exe/rubydb +23 -0
  125. data/lib/rubydb/backup/archive.rb +334 -0
  126. data/lib/rubydb/backup/backup.rb +401 -0
  127. data/lib/rubydb/backup/incremental.rb +353 -0
  128. data/lib/rubydb/backup/restore.rb +290 -0
  129. data/lib/rubydb/backup/snapshot.rb +267 -0
  130. data/lib/rubydb/backup/verification.rb +279 -0
  131. data/lib/rubydb/branching/branch.rb +181 -0
  132. data/lib/rubydb/branching/branch_manager.rb +311 -0
  133. data/lib/rubydb/branching/branch_metadata.rb +140 -0
  134. data/lib/rubydb/branching/checkout.rb +166 -0
  135. data/lib/rubydb/branching/copy_on_write.rb +272 -0
  136. data/lib/rubydb/branching/diff.rb +138 -0
  137. data/lib/rubydb/branching/merge.rb +285 -0
  138. data/lib/rubydb/build_info.rb +15 -0
  139. data/lib/rubydb/catalog/catalog.rb +391 -0
  140. data/lib/rubydb/catalog/column.rb +112 -0
  141. data/lib/rubydb/catalog/constraint.rb +180 -0
  142. data/lib/rubydb/catalog/database.rb +184 -0
  143. data/lib/rubydb/catalog/index.rb +97 -0
  144. data/lib/rubydb/catalog/schema.rb +103 -0
  145. data/lib/rubydb/catalog/sequence.rb +90 -0
  146. data/lib/rubydb/catalog/system_catalog.rb +698 -0
  147. data/lib/rubydb/catalog/table.rb +178 -0
  148. data/lib/rubydb/catalog/trigger.rb +102 -0
  149. data/lib/rubydb/catalog/view.rb +66 -0
  150. data/lib/rubydb/cli/application.rb +163 -0
  151. data/lib/rubydb/cli/commands/backup.rb +81 -0
  152. data/lib/rubydb/cli/commands/branch.rb +72 -0
  153. data/lib/rubydb/cli/commands/checkout.rb +54 -0
  154. data/lib/rubydb/cli/commands/create.rb +58 -0
  155. data/lib/rubydb/cli/commands/diff.rb +77 -0
  156. data/lib/rubydb/cli/commands/doctor.rb +74 -0
  157. data/lib/rubydb/cli/commands/drop.rb +57 -0
  158. data/lib/rubydb/cli/commands/init.rb +102 -0
  159. data/lib/rubydb/cli/commands/inspect.rb +95 -0
  160. data/lib/rubydb/cli/commands/merge.rb +63 -0
  161. data/lib/rubydb/cli/commands/migrate.rb +62 -0
  162. data/lib/rubydb/cli/commands/restart.rb +39 -0
  163. data/lib/rubydb/cli/commands/restore.rb +121 -0
  164. data/lib/rubydb/cli/commands/shell.rb +365 -0
  165. data/lib/rubydb/cli/commands/snapshot.rb +79 -0
  166. data/lib/rubydb/cli/commands/start.rb +82 -0
  167. data/lib/rubydb/cli/commands/status.rb +92 -0
  168. data/lib/rubydb/cli/commands/stop.rb +47 -0
  169. data/lib/rubydb/cli/commands/vacuum.rb +58 -0
  170. data/lib/rubydb/cli/formatter.rb +221 -0
  171. data/lib/rubydb/cli/output.rb +168 -0
  172. data/lib/rubydb/client/client.rb +304 -0
  173. data/lib/rubydb/client/connection.rb +415 -0
  174. data/lib/rubydb/client/connection_pool.rb +168 -0
  175. data/lib/rubydb/client/connection_url.rb +96 -0
  176. data/lib/rubydb/client/prepared_statement.rb +60 -0
  177. data/lib/rubydb/client/result.rb +123 -0
  178. data/lib/rubydb/client/statement.rb +52 -0
  179. data/lib/rubydb/client/transaction.rb +130 -0
  180. data/lib/rubydb/concurrency/concurrency.rb +19 -0
  181. data/lib/rubydb/concurrency/deadlock_detector.rb +150 -0
  182. data/lib/rubydb/concurrency/latch.rb +101 -0
  183. data/lib/rubydb/concurrency/lock_graph.rb +165 -0
  184. data/lib/rubydb/concurrency/mutex.rb +183 -0
  185. data/lib/rubydb/concurrency/rw_lock.rb +180 -0
  186. data/lib/rubydb/concurrency/scheduler.rb +250 -0
  187. data/lib/rubydb/concurrency/worker_pool.rb +143 -0
  188. data/lib/rubydb/configuration/config.rb +170 -0
  189. data/lib/rubydb/configuration/defaults.rb +179 -0
  190. data/lib/rubydb/configuration/environment.rb +152 -0
  191. data/lib/rubydb/configuration/parser.rb +185 -0
  192. data/lib/rubydb/configuration/validation.rb +221 -0
  193. data/lib/rubydb/constants.rb +74 -0
  194. data/lib/rubydb/constraints/check.rb +181 -0
  195. data/lib/rubydb/constraints/constraint.rb +101 -0
  196. data/lib/rubydb/constraints/foreign_key.rb +130 -0
  197. data/lib/rubydb/constraints/not_null.rb +64 -0
  198. data/lib/rubydb/constraints/primary_key.rb +99 -0
  199. data/lib/rubydb/constraints/unique.rb +108 -0
  200. data/lib/rubydb/constraints/validator.rb +350 -0
  201. data/lib/rubydb/errors/authentication_error.rb +10 -0
  202. data/lib/rubydb/errors/authorization_error.rb +23 -0
  203. data/lib/rubydb/errors/client_error.rb +10 -0
  204. data/lib/rubydb/errors/configuration_error.rb +10 -0
  205. data/lib/rubydb/errors/connection_error.rb +10 -0
  206. data/lib/rubydb/errors/constraint_error.rb +23 -0
  207. data/lib/rubydb/errors/corruption_error.rb +10 -0
  208. data/lib/rubydb/errors/database_error.rb +10 -0
  209. data/lib/rubydb/errors/error.rb +20 -0
  210. data/lib/rubydb/errors/execution_error.rb +10 -0
  211. data/lib/rubydb/errors/parser_error.rb +10 -0
  212. data/lib/rubydb/errors/recovery_error.rb +10 -0
  213. data/lib/rubydb/errors/replication_error.rb +10 -0
  214. data/lib/rubydb/errors/server_error.rb +6 -0
  215. data/lib/rubydb/errors/storage_error.rb +10 -0
  216. data/lib/rubydb/errors/transaction_error.rb +10 -0
  217. data/lib/rubydb/execution/aggregate_executor.rb +138 -0
  218. data/lib/rubydb/execution/delete_executor.rb +112 -0
  219. data/lib/rubydb/execution/distinct_executor.rb +135 -0
  220. data/lib/rubydb/execution/executor.rb +1192 -0
  221. data/lib/rubydb/execution/expression.rb +193 -0
  222. data/lib/rubydb/execution/index_scan.rb +142 -0
  223. data/lib/rubydb/execution/insert_executor.rb +217 -0
  224. data/lib/rubydb/execution/join_executor.rb +249 -0
  225. data/lib/rubydb/execution/limit_executor.rb +85 -0
  226. data/lib/rubydb/execution/optimizer.rb +215 -0
  227. data/lib/rubydb/execution/plan.rb +361 -0
  228. data/lib/rubydb/execution/planner.rb +546 -0
  229. data/lib/rubydb/execution/predicate.rb +235 -0
  230. data/lib/rubydb/execution/scan.rb +49 -0
  231. data/lib/rubydb/execution/sequential_scan.rb +63 -0
  232. data/lib/rubydb/execution/sort_executor.rb +185 -0
  233. data/lib/rubydb/execution/update_executor.rb +162 -0
  234. data/lib/rubydb/functions/aggregate.rb +70 -0
  235. data/lib/rubydb/functions/date_functions.rb +278 -0
  236. data/lib/rubydb/functions/function.rb +85 -0
  237. data/lib/rubydb/functions/json_functions.rb +215 -0
  238. data/lib/rubydb/functions/numeric_functions.rb +346 -0
  239. data/lib/rubydb/functions/scalar.rb +52 -0
  240. data/lib/rubydb/functions/string_functions.rb +383 -0
  241. data/lib/rubydb/functions/system_functions.rb +246 -0
  242. data/lib/rubydb/history/as_of.rb +238 -0
  243. data/lib/rubydb/history/change.rb +105 -0
  244. data/lib/rubydb/history/history.rb +131 -0
  245. data/lib/rubydb/history/history_manager.rb +229 -0
  246. data/lib/rubydb/history/temporal_query.rb +202 -0
  247. data/lib/rubydb/history/timeline.rb +144 -0
  248. data/lib/rubydb/indexes/btree.rb +186 -0
  249. data/lib/rubydb/indexes/btree_cursor.rb +258 -0
  250. data/lib/rubydb/indexes/btree_node.rb +385 -0
  251. data/lib/rubydb/indexes/hash_index.rb +150 -0
  252. data/lib/rubydb/indexes/index.rb +71 -0
  253. data/lib/rubydb/indexes/index_manager.rb +406 -0
  254. data/lib/rubydb/indexes/index_scan.rb +470 -0
  255. data/lib/rubydb/migrations/migration.rb +254 -0
  256. data/lib/rubydb/migrations/migration_lock.rb +146 -0
  257. data/lib/rubydb/migrations/migration_manager.rb +176 -0
  258. data/lib/rubydb/migrations/migration_version.rb +71 -0
  259. data/lib/rubydb/migrations/schema_diff.rb +211 -0
  260. data/lib/rubydb/migrations/schema_version.rb +64 -0
  261. data/lib/rubydb/monitoring/events.rb +160 -0
  262. data/lib/rubydb/monitoring/health.rb +222 -0
  263. data/lib/rubydb/monitoring/logger.rb +193 -0
  264. data/lib/rubydb/monitoring/metrics.rb +359 -0
  265. data/lib/rubydb/monitoring/performance.rb +176 -0
  266. data/lib/rubydb/monitoring/statistics.rb +170 -0
  267. data/lib/rubydb/mvcc/garbage_collector.rb +199 -0
  268. data/lib/rubydb/mvcc/mvcc.rb +16 -0
  269. data/lib/rubydb/mvcc/snapshot.rb +147 -0
  270. data/lib/rubydb/mvcc/vacuum.rb +180 -0
  271. data/lib/rubydb/mvcc/version.rb +106 -0
  272. data/lib/rubydb/mvcc/version_store.rb +398 -0
  273. data/lib/rubydb/mvcc/visibility.rb +109 -0
  274. data/lib/rubydb/protocol/capabilities.rb +125 -0
  275. data/lib/rubydb/protocol/decoder.rb +145 -0
  276. data/lib/rubydb/protocol/encoder.rb +136 -0
  277. data/lib/rubydb/protocol/handshake.rb +305 -0
  278. data/lib/rubydb/protocol/message.rb +121 -0
  279. data/lib/rubydb/protocol/protocol.rb +277 -0
  280. data/lib/rubydb/protocol/version.rb +54 -0
  281. data/lib/rubydb/rails/adapter.rb +239 -0
  282. data/lib/rubydb/rails/connection.rb +314 -0
  283. data/lib/rubydb/rails/database_statements.rb +122 -0
  284. data/lib/rubydb/rails/migration.rb +131 -0
  285. data/lib/rubydb/rails/quoting.rb +109 -0
  286. data/lib/rubydb/rails/result.rb +120 -0
  287. data/lib/rubydb/rails/schema_statements.rb +339 -0
  288. data/lib/rubydb/rails/transaction.rb +105 -0
  289. data/lib/rubydb/rails/type.rb +126 -0
  290. data/lib/rubydb/recovery/checkpoint.rb +257 -0
  291. data/lib/rubydb/recovery/consistency.rb +467 -0
  292. data/lib/rubydb/recovery/corruption_detector.rb +5 -0
  293. data/lib/rubydb/recovery/crash_recovery.rb +387 -0
  294. data/lib/rubydb/recovery/recovery_manager.rb +206 -0
  295. data/lib/rubydb/recovery/redo.rb +237 -0
  296. data/lib/rubydb/recovery/undo.rb +206 -0
  297. data/lib/rubydb/replication/failover.rb +5 -0
  298. data/lib/rubydb/replication/fencing.rb +63 -0
  299. data/lib/rubydb/replication/primary.rb +450 -0
  300. data/lib/rubydb/replication/replica.rb +384 -0
  301. data/lib/rubydb/replication/replication_log.rb +200 -0
  302. data/lib/rubydb/replication/replication_manager.rb +308 -0
  303. data/lib/rubydb/replication/replication_slot.rb +295 -0
  304. data/lib/rubydb/replication/replication_stream.rb +201 -0
  305. data/lib/rubydb/rubydb.rb +560 -0
  306. data/lib/rubydb/security/access_control.rb +254 -0
  307. data/lib/rubydb/security/audit_log.rb +213 -0
  308. data/lib/rubydb/security/authentication.rb +302 -0
  309. data/lib/rubydb/security/authorization.rb +282 -0
  310. data/lib/rubydb/security/credentials.rb +196 -0
  311. data/lib/rubydb/security/password.rb +215 -0
  312. data/lib/rubydb/security/permissions.rb +74 -0
  313. data/lib/rubydb/security/role.rb +101 -0
  314. data/lib/rubydb/security/user.rb +86 -0
  315. data/lib/rubydb/server/connection.rb +366 -0
  316. data/lib/rubydb/server/connection_pool.rb +193 -0
  317. data/lib/rubydb/server/lifecycle.rb +228 -0
  318. data/lib/rubydb/server/listener.rb +136 -0
  319. data/lib/rubydb/server/request_handler.rb +276 -0
  320. data/lib/rubydb/server/server.rb +364 -0
  321. data/lib/rubydb/server/session.rb +369 -0
  322. data/lib/rubydb/server/worker.rb +210 -0
  323. data/lib/rubydb/server/worker_pool.rb +168 -0
  324. data/lib/rubydb/sql/ast/alter_table.rb +169 -0
  325. data/lib/rubydb/sql/ast/begin_transaction.rb +47 -0
  326. data/lib/rubydb/sql/ast/commit.rb +37 -0
  327. data/lib/rubydb/sql/ast/constraint.rb +83 -0
  328. data/lib/rubydb/sql/ast/create_database.rb +41 -0
  329. data/lib/rubydb/sql/ast/create_index.rb +61 -0
  330. data/lib/rubydb/sql/ast/create_schema.rb +52 -0
  331. data/lib/rubydb/sql/ast/create_table.rb +187 -0
  332. data/lib/rubydb/sql/ast/delete.rb +54 -0
  333. data/lib/rubydb/sql/ast/drop_database.rb +41 -0
  334. data/lib/rubydb/sql/ast/drop_index.rb +41 -0
  335. data/lib/rubydb/sql/ast/drop_schema.rb +49 -0
  336. data/lib/rubydb/sql/ast/drop_table.rb +49 -0
  337. data/lib/rubydb/sql/ast/explain.rb +64 -0
  338. data/lib/rubydb/sql/ast/expression.rb +604 -0
  339. data/lib/rubydb/sql/ast/insert.rb +82 -0
  340. data/lib/rubydb/sql/ast/node.rb +42 -0
  341. data/lib/rubydb/sql/ast/rollback.rb +63 -0
  342. data/lib/rubydb/sql/ast/savepoint.rb +59 -0
  343. data/lib/rubydb/sql/ast/select.rb +88 -0
  344. data/lib/rubydb/sql/ast/set_operation.rb +20 -0
  345. data/lib/rubydb/sql/ast/trigger.rb +29 -0
  346. data/lib/rubydb/sql/ast/update.rb +88 -0
  347. data/lib/rubydb/sql/ast/vacuum.rb +19 -0
  348. data/lib/rubydb/sql/ast/view.rb +32 -0
  349. data/lib/rubydb/sql/ast/with.rb +32 -0
  350. data/lib/rubydb/sql/grammar.rb +86 -0
  351. data/lib/rubydb/sql/keywords.rb +156 -0
  352. data/lib/rubydb/sql/lexer.rb +214 -0
  353. data/lib/rubydb/sql/operators.rb +100 -0
  354. data/lib/rubydb/sql/parser.rb +1185 -0
  355. data/lib/rubydb/sql/planner/analyzer.rb +302 -0
  356. data/lib/rubydb/sql/planner/binder.rb +550 -0
  357. data/lib/rubydb/sql/planner/type_checker.rb +431 -0
  358. data/lib/rubydb/sql/token.rb +210 -0
  359. data/lib/rubydb/storage/buffer_frame.rb +44 -0
  360. data/lib/rubydb/storage/buffer_pool.rb +155 -0
  361. data/lib/rubydb/storage/database_lock.rb +74 -0
  362. data/lib/rubydb/storage/deserializer.rb +342 -0
  363. data/lib/rubydb/storage/engine.rb +2338 -0
  364. data/lib/rubydb/storage/file_manager.rb +187 -0
  365. data/lib/rubydb/storage/free_space_map.rb +81 -0
  366. data/lib/rubydb/storage/page.rb +94 -0
  367. data/lib/rubydb/storage/page_allocator.rb +855 -0
  368. data/lib/rubydb/storage/page_header.rb +67 -0
  369. data/lib/rubydb/storage/page_manager.rb +131 -0
  370. data/lib/rubydb/storage/record.rb +58 -0
  371. data/lib/rubydb/storage/row.rb +78 -0
  372. data/lib/rubydb/storage/serializer.rb +51 -0
  373. data/lib/rubydb/storage/storage_layout.rb +151 -0
  374. data/lib/rubydb/storage/storage_manager.rb +114 -0
  375. data/lib/rubydb/storage/tuple.rb +461 -0
  376. data/lib/rubydb/storage/visibility_map.rb +984 -0
  377. data/lib/rubydb/transactions/commit_manager.rb +220 -0
  378. data/lib/rubydb/transactions/isolation.rb +98 -0
  379. data/lib/rubydb/transactions/lock.rb +76 -0
  380. data/lib/rubydb/transactions/lock_manager.rb +362 -0
  381. data/lib/rubydb/transactions/savepoint.rb +143 -0
  382. data/lib/rubydb/transactions/transaction.rb +215 -0
  383. data/lib/rubydb/transactions/transaction_id.rb +84 -0
  384. data/lib/rubydb/transactions/transaction_log.rb +257 -0
  385. data/lib/rubydb/transactions/transaction_manager.rb +435 -0
  386. data/lib/rubydb/types/bigint.rb +36 -0
  387. data/lib/rubydb/types/blob.rb +37 -0
  388. data/lib/rubydb/types/boolean.rb +34 -0
  389. data/lib/rubydb/types/date.rb +39 -0
  390. data/lib/rubydb/types/decimal.rb +48 -0
  391. data/lib/rubydb/types/float.rb +34 -0
  392. data/lib/rubydb/types/integer.rb +36 -0
  393. data/lib/rubydb/types/json.rb +41 -0
  394. data/lib/rubydb/types/null.rb +34 -0
  395. data/lib/rubydb/types/smallint.rb +36 -0
  396. data/lib/rubydb/types/text.rb +37 -0
  397. data/lib/rubydb/types/time.rb +46 -0
  398. data/lib/rubydb/types/timestamp.rb +39 -0
  399. data/lib/rubydb/types/type.rb +119 -0
  400. data/lib/rubydb/types/uuid.rb +47 -0
  401. data/lib/rubydb/types/varchar.rb +37 -0
  402. data/lib/rubydb/version.rb +33 -0
  403. data/lib/rubydb/wal/archive.rb +193 -0
  404. data/lib/rubydb/wal/checkpoint.rb +183 -0
  405. data/lib/rubydb/wal/lsn.rb +94 -0
  406. data/lib/rubydb/wal/reader.rb +260 -0
  407. data/lib/rubydb/wal/record.rb +105 -0
  408. data/lib/rubydb/wal/segment.rb +193 -0
  409. data/lib/rubydb/wal/wal.rb +452 -0
  410. data/lib/rubydb/wal/writer.rb +236 -0
  411. data/lib/rubydb.rb +7 -0
  412. data/packaging/docker/Dockerfile +36 -0
  413. data/packaging/docker/docker-compose.failover.yml +43 -0
  414. data/packaging/docker/entrypoint.sh +15 -0
  415. data/packaging/homebrew/rubydb.rb +19 -0
  416. data/packaging/systemd/rubydb.service +22 -0
  417. data/rubydb.gemspec +57 -0
  418. data/scripts/benchmark +7 -0
  419. data/scripts/durability_drill +37 -0
  420. data/scripts/format +0 -0
  421. data/scripts/fuzz +63 -0
  422. data/scripts/lint +0 -0
  423. data/scripts/release +50 -0
  424. data/scripts/release_check +43 -0
  425. data/scripts/replication_failover_drill +250 -0
  426. data/scripts/replication_network_failover_drill +255 -0
  427. data/scripts/restore_drill +45 -0
  428. data/scripts/security +0 -0
  429. data/scripts/setup +0 -0
  430. data/scripts/test +0 -0
  431. data/scripts/test-chaos +0 -0
  432. data/scripts/test-crash +0 -0
  433. data/scripts/test-integration +0 -0
  434. data/scripts/test-unit +0 -0
  435. data/visibility_map.json +1 -0
  436. metadata +538 -0
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
4
+
5
+ module RubyDB
6
+ module History
7
+ # TemporalQuery - Executes temporal queries against history
8
+ class TemporalQuery
9
+ attr_reader :stats
10
+
11
+ def initialize(engine, history_manager, config = {})
12
+ @engine = engine
13
+ @history_manager = history_manager
14
+ @config = config
15
+ @stats = {
16
+ queries: 0,
17
+ temporal_queries: 0,
18
+ as_of_queries: 0,
19
+ between_queries: 0,
20
+ query_time_ms: 0,
21
+ avg_query_time_ms: 0
22
+ }
23
+ @lock = Monitor.new
24
+ @result_cache = {}
25
+ @cache_size = config[:cache_size] || 1000
26
+ end
27
+
28
+ def query_as_of(table_name, time, columns = nil)
29
+ @lock.synchronize do
30
+ started_at = Time.now
31
+ @stats[:queries] += 1
32
+ @stats[:as_of_queries] += 1
33
+
34
+ cache_key = "as_of_#{table_name}_#{time.to_i}_#{columns&.join('_')}"
35
+ if @result_cache.key?(cache_key)
36
+ return @result_cache[cache_key]
37
+ end
38
+
39
+ # Get history for the table
40
+ changes = @history_manager.get_changes_for_table(table_name, time)
41
+
42
+ # Reconstruct state at time
43
+ result = reconstruct_state(changes, time, columns)
44
+
45
+ # Cache result
46
+ if @result_cache.size < @cache_size
47
+ @result_cache[cache_key] = result
48
+ end
49
+
50
+ elapsed_ms = (Time.now - started_at) * 1000
51
+ @stats[:query_time_ms] += elapsed_ms
52
+ @stats[:avg_query_time_ms] = @stats[:query_time_ms] / @stats[:queries]
53
+
54
+ result
55
+ end
56
+ end
57
+
58
+ def query_between(table_name, start_time, end_time, columns = nil)
59
+ @lock.synchronize do
60
+ started_at = Time.now
61
+ @stats[:queries] += 1
62
+ @stats[:between_queries] += 1
63
+
64
+ # Get changes between times
65
+ changes = @history_manager.get_changes_between(table_name, start_time, end_time)
66
+
67
+ # Apply changes and return results
68
+ results = apply_changes(changes, start_time, end_time, columns)
69
+
70
+ elapsed_ms = (Time.now - started_at) * 1000
71
+ @stats[:query_time_ms] += elapsed_ms
72
+ @stats[:avg_query_time_ms] = @stats[:query_time_ms] / @stats[:queries]
73
+
74
+ results
75
+ end
76
+ end
77
+
78
+ def query_temporal(table_name, time, columns = nil)
79
+ query_as_of(table_name, time, columns)
80
+ end
81
+
82
+ def query_version(table_name, row_id, version_time)
83
+ @lock.synchronize do
84
+ started_at = Time.now
85
+ @stats[:queries] += 1
86
+ @stats[:temporal_queries] += 1
87
+
88
+ changes = @history_manager.get_changes_for_row(table_name, row_id)
89
+
90
+ # Find version at time
91
+ version = find_version_at_time(changes, row_id, version_time)
92
+
93
+ elapsed_ms = (Time.now - started_at) * 1000
94
+ @stats[:query_time_ms] += elapsed_ms
95
+ @stats[:avg_query_time_ms] = @stats[:query_time_ms] / @stats[:queries]
96
+
97
+ version
98
+ end
99
+ end
100
+
101
+ def query_history(table_name, row_id)
102
+ @lock.synchronize do
103
+ @stats[:queries] += 1
104
+
105
+ changes = @history_manager.get_changes_for_row(table_name, row_id)
106
+
107
+ {
108
+ row_id: row_id,
109
+ table: table_name,
110
+ history: changes.map(&:to_hash)
111
+ }
112
+ end
113
+ end
114
+
115
+ def clear_cache
116
+ @lock.synchronize do
117
+ @result_cache.clear
118
+ end
119
+ end
120
+
121
+ def stats
122
+ @lock.synchronize do
123
+ @stats.merge({
124
+ cache_size: @result_cache.size,
125
+ max_cache_size: @cache_size
126
+ })
127
+ end
128
+ end
129
+
130
+ private
131
+
132
+ def reconstruct_state(changes, time, columns)
133
+ # Reconstruct the state at a point in time
134
+ rows = {}
135
+ changes.each do |change|
136
+ case change.operation
137
+ when Change::OP_INSERT
138
+ rows[change.row_id] = change.new_values
139
+ when Change::OP_UPDATE
140
+ if rows.key?(change.row_id)
141
+ rows[change.row_id] = rows[change.row_id].merge(change.new_values)
142
+ end
143
+ when Change::OP_DELETE
144
+ rows.delete(change.row_id)
145
+ end
146
+ end
147
+
148
+ {
149
+ table: changes.first&.table_name,
150
+ as_of: time.iso8601,
151
+ rows: columns ? rows.values.map { |r| r.select { |k| columns.include?(k) } } : rows.values,
152
+ count: rows.size
153
+ }
154
+ end
155
+
156
+ def apply_changes(changes, start_time, end_time, columns)
157
+ results = []
158
+ current_state = {}
159
+
160
+ changes.each do |change|
161
+ case change.operation
162
+ when Change::OP_INSERT
163
+ current_state[change.row_id] = change.new_values
164
+ when Change::OP_UPDATE
165
+ if current_state.key?(change.row_id)
166
+ current_state[change.row_id] = current_state[change.row_id].merge(change.new_values)
167
+ end
168
+ when Change::OP_DELETE
169
+ current_state.delete(change.row_id)
170
+ end
171
+
172
+ # Record state at this point
173
+ if change.timestamp >= start_time && change.timestamp <= end_time
174
+ results << {
175
+ timestamp: change.timestamp.iso8601,
176
+ operation: change.operation,
177
+ row_id: change.row_id,
178
+ data: columns ? change.new_values.select { |k| columns.include?(k) } : change.new_values
179
+ }
180
+ end
181
+ end
182
+
183
+ results
184
+ end
185
+
186
+ def find_version_at_time(changes, row_id, time)
187
+ # Find the version of a row at a specific time
188
+ current = nil
189
+ changes.each do |change|
190
+ if change.row_id == row_id && change.timestamp <= time
191
+ if change.operation == Change::OP_DELETE
192
+ current = nil
193
+ else
194
+ current = change.new_values.dup
195
+ end
196
+ end
197
+ end
198
+ current
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module RubyDB
6
+ module History
7
+ # Timeline - Manages a timeline of changes
8
+ class Timeline
9
+ attr_reader :name, :changes, :created_at, :updated_at
10
+
11
+ def initialize(name, changes = [])
12
+ @name = name
13
+ @changes = changes
14
+ @created_at = Time.now
15
+ @updated_at = Time.now
16
+ @position = changes.size
17
+ @lock = Mutex.new
18
+ end
19
+
20
+ def add_change(change)
21
+ @lock.synchronize do
22
+ @changes << change
23
+ @position = @changes.size
24
+ @updated_at = Time.now
25
+ end
26
+ end
27
+
28
+ def get_change_at(index)
29
+ @lock.synchronize do
30
+ @changes[index]
31
+ end
32
+ end
33
+
34
+ def get_changes_between(start_time, end_time)
35
+ @lock.synchronize do
36
+ @changes.select do |c|
37
+ c.timestamp >= start_time && c.timestamp <= end_time
38
+ end
39
+ end
40
+ end
41
+
42
+ def get_changes_for_table(table_name)
43
+ @lock.synchronize do
44
+ @changes.select { |c| c.table_name == table_name }
45
+ end
46
+ end
47
+
48
+ def get_changes_for_row(table_name, row_id)
49
+ @lock.synchronize do
50
+ @changes.select { |c| c.table_name == table_name && c.row_id == row_id }
51
+ end
52
+ end
53
+
54
+ def get_changes_by_operation(operation)
55
+ @lock.synchronize do
56
+ @changes.select { |c| c.operation == operation }
57
+ end
58
+ end
59
+
60
+ def get_changes_by_user(user)
61
+ @lock.synchronize do
62
+ @changes.select { |c| c.user == user }
63
+ end
64
+ end
65
+
66
+ def get_changes_by_branch(branch)
67
+ @lock.synchronize do
68
+ @changes.select { |c| c.branch == branch }
69
+ end
70
+ end
71
+
72
+ def get_changes_by_lsn_range(start_lsn, end_lsn)
73
+ @lock.synchronize do
74
+ @changes.select { |c| c.lsn >= start_lsn && c.lsn <= end_lsn }
75
+ end
76
+ end
77
+
78
+ def rollback_to(index)
79
+ @lock.synchronize do
80
+ return false if index < 0 || index >= @changes.size
81
+
82
+ # Remove changes after index
83
+ @changes = @changes[0..index]
84
+ @position = @changes.size
85
+ @updated_at = Time.now
86
+
87
+ true
88
+ end
89
+ end
90
+
91
+ def rollback_to_time(time)
92
+ @lock.synchronize do
93
+ index = @changes.rindex { |c| c.timestamp <= time }
94
+ return false unless index
95
+
96
+ rollback_to(index)
97
+ end
98
+ end
99
+
100
+ def size
101
+ @changes.size
102
+ end
103
+
104
+ def empty?
105
+ @changes.empty?
106
+ end
107
+
108
+ def last_change
109
+ @changes.last
110
+ end
111
+
112
+ def first_change
113
+ @changes.first
114
+ end
115
+
116
+ def to_hash
117
+ {
118
+ name: @name,
119
+ changes: @changes.map(&:to_hash),
120
+ created_at: @created_at.iso8601,
121
+ updated_at: @updated_at.iso8601,
122
+ size: @changes.size
123
+ }
124
+ end
125
+
126
+ def to_json
127
+ JSON.generate(to_hash)
128
+ end
129
+
130
+ def self.from_json(json_data)
131
+ data = JSON.parse(json_data, symbolize_names: true)
132
+ changes = data[:changes].map { |c| Change.from_json(c.to_json) }
133
+ timeline = new(data[:name], changes)
134
+ timeline.instance_variable_set(:@created_at, Time.parse(data[:created_at]))
135
+ timeline.instance_variable_set(:@updated_at, Time.parse(data[:updated_at]))
136
+ timeline
137
+ end
138
+
139
+ def inspect
140
+ "#<Timeline name=#{@name} changes=#{@changes.size}>"
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
4
+
5
+ module RubyDB
6
+ module Indexes
7
+ # B-Tree index implementation
8
+ class BTree < Index
9
+ attr_reader :root, :order, :height
10
+
11
+ def initialize(name, table_name, columns, options = {})
12
+ super(name, table_name, columns, options)
13
+ @order = options[:order] || 4
14
+ @height = 0
15
+ @root = nil
16
+ @node_pages = {}
17
+ @next_page = 1000
18
+ @lock = Monitor.new
19
+
20
+ # Initialize root node
21
+ initialize_root
22
+ end
23
+
24
+ def insert(key, row_id)
25
+ @lock.synchronize do
26
+ if @root.nil?
27
+ initialize_root
28
+ end
29
+
30
+ result = @root.insert(key, row_id)
31
+
32
+ # Handle root split
33
+ if result.is_a?(Array) && result.size == 3
34
+ left, right, split_key = result
35
+
36
+ # Create new root
37
+ new_root = BTreeNode.new(allocate_page, false, @order, method(:allocate_page))
38
+ new_root.keys = [split_key]
39
+ new_root.children = [left, right]
40
+ left.parent = new_root
41
+ right.parent = new_root
42
+
43
+ @root = new_root
44
+ @height += 1
45
+ end
46
+
47
+ @entries_count += 1
48
+ @modified_at = Time.now
49
+ true
50
+ end
51
+ end
52
+
53
+ def delete(key, row_id)
54
+ @lock.synchronize do
55
+ return false if @root.nil?
56
+
57
+ result = @root.delete(key)
58
+
59
+ # Handle root underflow
60
+ if result == :underflow && @root.keys.empty?
61
+ if @root.is_leaf
62
+ @root = nil
63
+ @height = 0
64
+ else
65
+ @root = @root.children.first
66
+ @root.parent = nil
67
+ @height -= 1
68
+ end
69
+ end
70
+
71
+ @entries_count -= 1 if result
72
+ @modified_at = Time.now
73
+ result
74
+ end
75
+ end
76
+
77
+ def search(key)
78
+ @lock.synchronize do
79
+ return nil if @root.nil?
80
+ @root.search(key)
81
+ end
82
+ end
83
+
84
+ def range_search(start_key, end_key)
85
+ @lock.synchronize do
86
+ return [] if @root.nil?
87
+ @root.range_search(start_key, end_key)
88
+ end
89
+ end
90
+
91
+ def build(rows)
92
+ @lock.synchronize do
93
+ clear
94
+
95
+ rows.each do |row|
96
+ key = extract_key(row)
97
+ row_id = row[:_row_id] || row["id"] || row[:id]
98
+ insert(key, row_id)
99
+ end
100
+
101
+ @is_built = true
102
+ @modified_at = Time.now
103
+ true
104
+ end
105
+ end
106
+
107
+ def clear
108
+ @lock.synchronize do
109
+ @root = nil
110
+ @node_pages.clear
111
+ @height = 0
112
+ @entries_count = 0
113
+ initialize_root
114
+ true
115
+ end
116
+ end
117
+
118
+ def validate
119
+ return true if @root.nil?
120
+
121
+ # Check B-Tree properties
122
+ check_node(@root, nil, nil)
123
+ true
124
+ end
125
+
126
+ def analyze
127
+ super.merge({
128
+ order: @order,
129
+ height: @height,
130
+ nodes: @node_pages.size,
131
+ root_page: @root&.page_number,
132
+ is_built: @is_built
133
+ })
134
+ end
135
+
136
+ private
137
+
138
+ def initialize_root
139
+ @root = BTreeNode.new(allocate_page, true, @order, method(:allocate_page))
140
+ @height = 1
141
+ @is_built = false
142
+ end
143
+
144
+ def allocate_page
145
+ page = @next_page
146
+ @next_page += 1
147
+ @node_pages[page] = true
148
+ page
149
+ end
150
+
151
+ def extract_key(row)
152
+ if @columns.size == 1
153
+ column = @columns.first
154
+ row.key?(column) ? row[column] : row[column.to_s]
155
+ else
156
+ @columns.map { |col| row.key?(col) ? row[col] : row[col.to_s] }
157
+ end
158
+ end
159
+
160
+ def check_node(node, min_key, max_key)
161
+ # Check keys are in order
162
+ (0...node.keys.size - 1).each do |i|
163
+ raise "B-Tree invariant violated: keys out of order" if node.keys[i] > node.keys[i + 1]
164
+ end
165
+
166
+ # Check min/max constraints
167
+ if min_key && node.keys.first < min_key
168
+ raise "B-Tree invariant violated: key < min"
169
+ end
170
+
171
+ if max_key && node.keys.last > max_key
172
+ raise "B-Tree invariant violated: key > max"
173
+ end
174
+
175
+ # Check children
176
+ unless node.is_leaf
177
+ node.children.each_with_index do |child, i|
178
+ child_min = i == 0 ? min_key : node.keys[i - 1]
179
+ child_max = i == node.children.size - 1 ? max_key : node.keys[i]
180
+ check_node(child, child_min, child_max)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end