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,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when a constraint is violated
5
+ class ConstraintError < Error
6
+ attr_reader :constraint_name, :table_name, :column_name
7
+
8
+ def initialize(message = "Constraint violation", constraint_name: nil, table_name: nil, column_name: nil, details: nil)
9
+ super(message, code: ErrorCodes::CONSTRAINT_VIOLATION, details: details)
10
+ @constraint_name = constraint_name
11
+ @table_name = table_name
12
+ @column_name = column_name
13
+ end
14
+
15
+ def to_s
16
+ msg = super
17
+ msg = "#{msg} on table #{@table_name}" if @table_name
18
+ msg = "#{msg} column #{@column_name}" if @column_name
19
+ msg = "#{msg} constraint #{@constraint_name}" if @constraint_name
20
+ msg
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when database corruption is detected
5
+ class CorruptionError < Error
6
+ def initialize(message = "Database corruption detected", details: nil)
7
+ super(message, code: ErrorCodes::CORRUPTION, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when there is a general database error
5
+ class DatabaseError < Error
6
+ def initialize(message = "Database error", details: nil)
7
+ super(message, code: ErrorCodes::ERROR, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Base error class for all RubyDB errors
5
+ class Error < StandardError
6
+ attr_reader :code, :details
7
+
8
+ def initialize(message = nil, code: nil, details: nil)
9
+ super(message)
10
+ @code = code || ErrorCodes::ERROR
11
+ @details = details || {}
12
+ end
13
+
14
+ def to_s
15
+ msg = super
16
+ msg = "#{msg} (code: #{@code})" if @code
17
+ msg
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when query execution fails
5
+ class ExecutionError < Error
6
+ def initialize(message = "Execution error", code: ErrorCodes::ERROR, details: nil)
7
+ super(message, code: code, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when SQL parsing fails
5
+ class ParserError < Error
6
+ def initialize(message = "SQL parsing error", details: nil)
7
+ super(message, code: ErrorCodes::ERROR, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when recovery from crash fails
5
+ class RecoveryError < Error
6
+ def initialize(message = "Recovery failed", details: nil)
7
+ super(message, code: ErrorCodes::ERROR, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when there is an error with replication
5
+ class ReplicationError < Error
6
+ def initialize(message = "Replication error", details: nil)
7
+ super(message, code: ErrorCodes::ERROR, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ class ServerError < Error
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when there is an error with storage operations
5
+ class StorageError < Error
6
+ def initialize(message = "Storage error", details: nil)
7
+ super(message, code: ErrorCodes::IO_ERROR, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when there is an error with transactions
5
+ class TransactionError < Error
6
+ def initialize(message = "Transaction error", details: nil)
7
+ super(message, code: ErrorCodes::TRANSACTION_ABORTED, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # AggregateExecutor - Executes aggregate operations (GROUP BY, aggregate functions)
6
+ class AggregateExecutor
7
+ attr_reader :engine, :stats
8
+
9
+ def initialize(engine)
10
+ @engine = engine
11
+ @stats = {
12
+ aggregations: 0,
13
+ groups_produced: 0,
14
+ total_time_ms: 0
15
+ }
16
+ @lock = Mutex.new
17
+ end
18
+
19
+ def execute(plan, transaction_id = nil)
20
+ @lock.synchronize do
21
+ start_time = Time.now
22
+ @stats[:aggregations] += 1
23
+
24
+ # Get input rows
25
+ rows = plan.input_rows || []
26
+ if rows.empty? && plan.input_plan
27
+ executor = Executor.new(@engine)
28
+ result = executor.execute(plan.input_plan, transaction_id)
29
+ rows = result[:rows] if result
30
+ end
31
+
32
+ group_by = plan.group_by || []
33
+ aggregates = plan.aggregates || []
34
+
35
+ # Perform aggregation
36
+ result = if group_by.empty?
37
+ # Global aggregation
38
+ aggregate_globally(rows, aggregates)
39
+ else
40
+ # Grouped aggregation
41
+ aggregate_by_groups(rows, group_by, aggregates)
42
+ end
43
+
44
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
45
+ @stats[:groups_produced] += result.size
46
+ @stats[:total_time_ms] += elapsed_ms
47
+
48
+ {
49
+ rows: result,
50
+ row_count: result.size,
51
+ groups: group_by,
52
+ aggregates: aggregates,
53
+ message: "AGGREGATE produced #{result.size} groups"
54
+ }
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def aggregate_globally(rows, aggregates)
61
+ return [] if rows.empty? && aggregates.empty?
62
+
63
+ result = {}
64
+
65
+ aggregates.each do |agg|
66
+ values = rows.map { |row| row[agg.column.to_s] }.compact
67
+ result[agg.alias || agg.name] = apply_aggregate(agg, values)
68
+ end
69
+
70
+ [result]
71
+ end
72
+
73
+ def aggregate_by_groups(rows, group_by, aggregates)
74
+ groups = {}
75
+
76
+ rows.each do |row|
77
+ # Build group key
78
+ key = group_by.map { |col| row[col.to_s] }
79
+ key_str = key.join("||")
80
+
81
+ groups[key_str] ||= {
82
+ key: key,
83
+ rows: [],
84
+ values: {}
85
+ }
86
+ groups[key_str][:rows] << row
87
+ end
88
+
89
+ result = []
90
+ groups.each do |_key_str, group|
91
+ row_result = {}
92
+
93
+ # Add group by columns
94
+ group_by.each_with_index do |col, idx|
95
+ row_result[col.to_s] = group[:key][idx]
96
+ end
97
+
98
+ # Apply aggregates
99
+ aggregates.each do |agg|
100
+ values = group[:rows].map { |r| r[agg.column.to_s] }.compact
101
+ row_result[agg.alias || agg.name] = apply_aggregate(agg, values)
102
+ end
103
+
104
+ result << row_result
105
+ end
106
+
107
+ result
108
+ end
109
+
110
+ def apply_aggregate(agg, values)
111
+ case agg.name.to_s.upcase
112
+ when "COUNT"
113
+ values.size
114
+ when "SUM"
115
+ values.sum
116
+ when "AVG"
117
+ values.empty? ? 0 : values.sum / values.size.to_f
118
+ when "MIN"
119
+ values.min
120
+ when "MAX"
121
+ values.max
122
+ when "GROUP_CONCAT"
123
+ values.join(",")
124
+ when "ARRAY_AGG"
125
+ values
126
+ when "STRING_AGG"
127
+ values.join(agg.separator || ",")
128
+ else
129
+ nil
130
+ end
131
+ end
132
+
133
+ def stats
134
+ @stats
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # DeleteExecutor - Executes DELETE operations
6
+ class DeleteExecutor
7
+ attr_reader :engine, :stats
8
+
9
+ def initialize(engine)
10
+ @engine = engine
11
+ @stats = {
12
+ deletes: 0,
13
+ rows_deleted: 0,
14
+ total_time_ms: 0
15
+ }
16
+ @lock = Mutex.new
17
+ end
18
+
19
+ def execute(plan, transaction_id = nil)
20
+ @lock.synchronize do
21
+ start_time = Time.now
22
+ @stats[:deletes] += 1
23
+
24
+ table_name = plan.table_name
25
+ predicate = plan.predicate
26
+
27
+ # Get table columns
28
+ table_columns = @engine.table_columns(table_name)
29
+ raise ExecutionError, "Table '#{table_name}' does not exist" if table_columns.empty?
30
+
31
+ # Find rows to delete
32
+ rows = find_rows(table_name, predicate)
33
+
34
+ deleted_count = 0
35
+ deleted_rows = []
36
+
37
+ rows.each do |row|
38
+ row_id = row[:_row_id] || row["_row_id"] || row[:id] || row["id"]
39
+
40
+ # Delete from engine
41
+ @engine.delete_row(table_name, row_id, transaction_id: transaction_id)
42
+
43
+ deleted_count += 1
44
+ deleted_rows << row
45
+ end
46
+
47
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
48
+ @stats[:rows_deleted] += deleted_count
49
+ @stats[:total_time_ms] += elapsed_ms
50
+
51
+ {
52
+ rows: deleted_rows,
53
+ row_count: deleted_count,
54
+ row_ids: deleted_rows.map { |r| r[:_row_id] || r["_row_id"] },
55
+ message: "DELETE #{deleted_count}"
56
+ }
57
+ end
58
+ end
59
+
60
+ def execute_batch(plan, row_ids, transaction_id = nil)
61
+ @lock.synchronize do
62
+ start_time = Time.now
63
+ @stats[:deletes] += 1
64
+
65
+ table_name = plan.table_name
66
+ table_columns = @engine.table_columns(table_name)
67
+
68
+ deleted_count = 0
69
+ deleted_rows = []
70
+
71
+ row_ids.each do |row_id|
72
+ row = @engine.select_row(table_name, row_id, table_columns)
73
+ next unless row
74
+
75
+ @engine.delete_row(table_name, row_id, transaction_id: transaction_id)
76
+
77
+ deleted_count += 1
78
+ deleted_rows << row
79
+ end
80
+
81
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
82
+ @stats[:rows_deleted] += deleted_count
83
+ @stats[:total_time_ms] += elapsed_ms
84
+
85
+ {
86
+ rows: deleted_rows,
87
+ row_count: deleted_count,
88
+ row_ids: row_ids,
89
+ message: "DELETE #{deleted_count}"
90
+ }
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def find_rows(table_name, predicate)
97
+ table_columns = @engine.table_columns(table_name)
98
+ rows = @engine.select_rows(table_name, table_columns)
99
+
100
+ if predicate
101
+ rows.select { |row| predicate.evaluate(row) }
102
+ else
103
+ rows
104
+ end
105
+ end
106
+
107
+ def stats
108
+ @stats
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module RubyDB
6
+ module Execution
7
+ # DistinctExecutor - Executes DISTINCT operations
8
+ class DistinctExecutor
9
+ attr_reader :engine, :stats
10
+
11
+ def initialize(engine)
12
+ @engine = engine
13
+ @stats = {
14
+ distincts: 0,
15
+ rows_input: 0,
16
+ rows_output: 0,
17
+ total_time_ms: 0
18
+ }
19
+ @lock = Mutex.new
20
+ end
21
+
22
+ def execute(plan, transaction_id = nil)
23
+ @lock.synchronize do
24
+ start_time = Time.now
25
+ @stats[:distincts] += 1
26
+
27
+ # Get input rows
28
+ rows = plan.input_rows || []
29
+ if rows.empty? && plan.input_plan
30
+ executor = Executor.new(@engine)
31
+ result = executor.execute(plan.input_plan, transaction_id)
32
+ rows = result[:rows] if result
33
+ end
34
+
35
+ distinct_columns = plan.distinct_columns || []
36
+
37
+ @stats[:rows_input] += rows.size
38
+
39
+ # Perform distinct
40
+ result_rows = if distinct_columns.empty?
41
+ # Distinct on all columns
42
+ distinct_all(rows)
43
+ else
44
+ # Distinct on specific columns
45
+ distinct_on_columns(rows, distinct_columns)
46
+ end
47
+
48
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
49
+ @stats[:rows_output] += result_rows.size
50
+ @stats[:total_time_ms] += elapsed_ms
51
+
52
+ {
53
+ rows: result_rows,
54
+ row_count: result_rows.size,
55
+ distinct_columns: distinct_columns,
56
+ total_input: rows.size,
57
+ message: "DISTINCT produced #{result_rows.size} rows"
58
+ }
59
+ end
60
+ end
61
+
62
+ def distinct_all(rows)
63
+ seen = Set.new
64
+ result = []
65
+
66
+ rows.each do |row|
67
+ # Create key from row values
68
+ key = row.values_at(*row.keys).join("||")
69
+ unless seen.include?(key)
70
+ seen.add(key)
71
+ result << row
72
+ end
73
+ end
74
+
75
+ result
76
+ end
77
+
78
+ def distinct_on_columns(rows, columns)
79
+ seen = Set.new
80
+ result = []
81
+
82
+ columns = columns.map(&:to_s)
83
+
84
+ rows.each do |row|
85
+ # Create key from specified columns
86
+ key = columns.map { |col| row[col] }.join("||")
87
+ unless seen.include?(key)
88
+ seen.add(key)
89
+ result << row
90
+ end
91
+ end
92
+
93
+ result
94
+ end
95
+
96
+ # Optimized distinct using hash aggregation
97
+ def distinct_using_hash(rows, columns = [])
98
+ hash = {}
99
+ columns = columns.map(&:to_s)
100
+
101
+ rows.each do |row|
102
+ key = if columns.empty?
103
+ row.values_at(*row.keys).join("||")
104
+ else
105
+ columns.map { |col| row[col] }.join("||")
106
+ end
107
+
108
+ hash[key] = row unless hash.key?(key)
109
+ end
110
+
111
+ hash.values
112
+ end
113
+
114
+ # Distinct with ordering
115
+ def distinct_with_order(rows, columns, order_by)
116
+ distinct_rows = distinct_on_columns(rows, columns)
117
+
118
+ # Sort results
119
+ sort_executor = SortExecutor.new(@engine)
120
+ sort_plan = Plan::Select.new(nil)
121
+ sort_plan.input_rows = distinct_rows
122
+ sort_plan.set_order_by(order_by)
123
+
124
+ result = sort_executor.execute(sort_plan)
125
+ result[:rows]
126
+ end
127
+
128
+ private
129
+
130
+ def stats
131
+ @stats
132
+ end
133
+ end
134
+ end
135
+ end