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,369 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require "time"
5
+
6
+ module RubyDB
7
+ module Server
8
+ # Session - Represents a client session
9
+ class Session
10
+ # A request-scoped cancellation token. Cancellation is deliberately
11
+ # cooperative: executors observe it at bounded points and unwind the
12
+ # request without killing a Ruby thread while it owns engine state.
13
+ class CancellationToken
14
+ def initialize
15
+ @lock = Mutex.new
16
+ @cancelled = false
17
+ end
18
+
19
+ def cancel!
20
+ @lock.synchronize { @cancelled = true }
21
+ end
22
+
23
+ def cancelled?
24
+ @lock.synchronize { @cancelled }
25
+ end
26
+ end
27
+
28
+ attr_reader :id, :connection, :created_at, :last_activity
29
+ attr_reader :username, :database, :transaction
30
+
31
+ def initialize(connection, config = {})
32
+ @id = generate_session_id
33
+ @connection = connection
34
+ @config = config
35
+ @created_at = Time.now
36
+ @last_activity = Time.now
37
+ @username = nil
38
+ @database = nil
39
+ @permissions = nil
40
+ @transaction = nil
41
+ @prepared_statements = {}
42
+ @cursors = {}
43
+ @variables = {}
44
+ @is_active = true
45
+ @lock = Mutex.new
46
+ @operations_lock = Mutex.new
47
+ @operations = {}
48
+ @pending_cancellations = {}
49
+ end
50
+
51
+ def authenticate(credentials)
52
+ @lock.synchronize do
53
+ @username = credentials[:username]
54
+ @database = credentials[:database] || "rubydb"
55
+ @permissions = resolve_permissions(@username)
56
+ @last_activity = Time.now
57
+ true
58
+ end
59
+ end
60
+
61
+ def process(request)
62
+ return process_cancel(request) if request[:type].to_s == "cancel"
63
+
64
+ @lock.synchronize do
65
+ @last_activity = Time.now
66
+ if request[:deadline_at] && Time.now >= Time.parse(request[:deadline_at].to_s)
67
+ return { success: false, error: "Request deadline exceeded before execution", code: "deadline_exceeded" }
68
+ end
69
+
70
+ request_id = request[:request_id].to_s
71
+ request_id = "local_#{object_id}_#{Time.now.to_f}" if request_id.empty?
72
+ cancellation = CancellationToken.new
73
+ @operations_lock.synchronize do
74
+ @operations[request_id] = cancellation
75
+ cancellation.cancel! if @pending_cancellations.delete(request_id)
76
+ end
77
+
78
+ begin
79
+ case request[:type]
80
+ when "query"
81
+ process_query(request[:sql], request[:params] || [], request[:deadline_at], cancellation)
82
+ when "prepare"
83
+ process_prepare(request[:sql])
84
+ when "execute"
85
+ process_execute(request[:statement_id], request[:params] || [], request[:deadline_at], cancellation)
86
+ when "close"
87
+ process_close(request[:statement_id])
88
+ when "begin"
89
+ process_begin
90
+ when "commit"
91
+ process_commit
92
+ when "rollback"
93
+ process_rollback
94
+ when "ping"
95
+ process_ping
96
+ else
97
+ { success: false, error: "Unknown request type: #{request[:type]}" }
98
+ end
99
+ rescue RubyDB::ExecutionError => error
100
+ raise unless %w[deadline_exceeded cancelled].include?(error.code.to_s)
101
+
102
+ { success: false, error: error.message, code: error.code.to_s }
103
+ ensure
104
+ @operations_lock.synchronize { @operations.delete(request_id) }
105
+ end
106
+ end
107
+ end
108
+
109
+ def cancel(request_id, allow_pending: false)
110
+ token = @operations_lock.synchronize do
111
+ key = request_id.to_s
112
+ current = @operations[key]
113
+ @pending_cancellations[key] = true if !current && allow_pending
114
+ current
115
+ end
116
+
117
+ token&.cancel!
118
+ true
119
+ end
120
+
121
+ def close
122
+ @lock.synchronize do
123
+ @is_active = false
124
+
125
+ # Close all prepared statements
126
+ @prepared_statements.each do |id, stmt|
127
+ stmt[:close].call if stmt[:close]
128
+ end
129
+ @prepared_statements.clear
130
+
131
+ # Close all cursors
132
+ @cursors.each do |id, cursor|
133
+ cursor[:close].call if cursor[:close]
134
+ end
135
+ @cursors.clear
136
+
137
+ # Rollback transaction if active
138
+ if @transaction && @transaction[:active]
139
+ @transaction[:rollback].call if @transaction[:rollback]
140
+ end
141
+ @transaction = nil
142
+
143
+ true
144
+ end
145
+ end
146
+
147
+ def active?
148
+ @is_active
149
+ end
150
+
151
+ def to_hash
152
+ {
153
+ id: @id,
154
+ username: @username,
155
+ database: @database,
156
+ created_at: @created_at.iso8601,
157
+ last_activity: @last_activity.iso8601,
158
+ prepared_statements: @prepared_statements.size,
159
+ cursors: @cursors.size,
160
+ variables: @variables.size,
161
+ in_transaction: @transaction && @transaction[:active],
162
+ active: @is_active
163
+ }
164
+ end
165
+
166
+ private
167
+
168
+ def generate_session_id
169
+ "sess_#{Time.now.to_i}_#{SecureRandom.hex(8)}"
170
+ end
171
+
172
+ def process_cancel(request)
173
+ target = request[:target_request_id] || request[:request_id]
174
+ cancelled = cancel(target, allow_pending: request[:allow_pending_cancellation] == true)
175
+ {
176
+ success: cancelled,
177
+ type: "cancel_response",
178
+ target_request_id: target,
179
+ cancelled: cancelled,
180
+ code: cancelled ? "cancel_requested" : "request_not_found",
181
+ timestamp: Time.now.iso8601
182
+ }
183
+ end
184
+
185
+ def process_query(sql, params, deadline_at = nil, cancellation = nil)
186
+ permission_error = authorize_sql(sql)
187
+ return permission_error if permission_error
188
+
189
+ {
190
+ success: true,
191
+ type: "query_result",
192
+ result: execute_sql(sql, params, deadline_at: deadline_at, cancellation: cancellation),
193
+ timestamp: Time.now.iso8601
194
+ }
195
+ end
196
+
197
+ def process_prepare(sql)
198
+ permission_error = authorize_sql(sql)
199
+ return permission_error if permission_error
200
+
201
+ stmt_id = "stmt_#{Time.now.to_i}_#{SecureRandom.hex(4)}"
202
+ @prepared_statements[stmt_id] = {
203
+ sql: sql,
204
+ created_at: Time.now,
205
+ close: lambda { @prepared_statements.delete(stmt_id) }
206
+ }
207
+
208
+ {
209
+ success: true,
210
+ type: "prepare_result",
211
+ statement_id: stmt_id,
212
+ timestamp: Time.now.iso8601
213
+ }
214
+ end
215
+
216
+ def process_execute(stmt_id, params, deadline_at = nil, cancellation = nil)
217
+ stmt = @prepared_statements[stmt_id]
218
+ unless stmt
219
+ return {
220
+ success: false,
221
+ error: "Statement not found: #{stmt_id}"
222
+ }
223
+ end
224
+
225
+ {
226
+ success: true,
227
+ type: "execute_result",
228
+ result: execute_sql(stmt[:sql], params, deadline_at: deadline_at, cancellation: cancellation),
229
+ timestamp: Time.now.iso8601
230
+ }
231
+ end
232
+
233
+ def process_close(stmt_id)
234
+ @prepared_statements.delete(stmt_id)
235
+
236
+ {
237
+ success: true,
238
+ type: "close_result",
239
+ timestamp: Time.now.iso8601
240
+ }
241
+ end
242
+
243
+ def process_begin
244
+ if @transaction && @transaction[:active]
245
+ return {
246
+ success: false,
247
+ error: "Transaction already active"
248
+ }
249
+ end
250
+
251
+ engine = @config[:engine]
252
+ transaction_id = engine&.begin_transaction || "txn_#{Time.now.to_i}"
253
+ @transaction = {
254
+ id: transaction_id,
255
+ started_at: Time.now,
256
+ active: true,
257
+ rollback: lambda do
258
+ engine&.rollback_transaction
259
+ @transaction = nil
260
+ end
261
+ }
262
+
263
+ {
264
+ success: true,
265
+ type: "begin_result",
266
+ transaction_id: @transaction[:id],
267
+ timestamp: Time.now.iso8601
268
+ }
269
+ end
270
+
271
+ def process_commit
272
+ unless @transaction && @transaction[:active]
273
+ return {
274
+ success: false,
275
+ error: "No active transaction"
276
+ }
277
+ end
278
+
279
+ committed = @config[:engine]&.commit_transaction
280
+ unless committed
281
+ return {
282
+ success: false,
283
+ error: "Transaction commit failed",
284
+ commit_ack: @config[:engine]&.last_commit_ack
285
+ }
286
+ end
287
+ @transaction[:active] = false
288
+ @transaction = nil
289
+
290
+ {
291
+ success: true,
292
+ type: "commit_result",
293
+ committed: true,
294
+ durable: @config[:engine]&.last_commit_ack&.fetch(:status, nil) == :durable,
295
+ commit_ack: @config[:engine]&.last_commit_ack,
296
+ timestamp: Time.now.iso8601
297
+ }
298
+ end
299
+
300
+ def process_rollback
301
+ unless @transaction && @transaction[:active]
302
+ return {
303
+ success: false,
304
+ error: "No active transaction"
305
+ }
306
+ end
307
+
308
+ @transaction[:active] = false
309
+ @config[:engine]&.rollback_transaction
310
+ @transaction = nil
311
+
312
+ {
313
+ success: true,
314
+ type: "rollback_result",
315
+ timestamp: Time.now.iso8601
316
+ }
317
+ end
318
+
319
+ def process_ping
320
+ {
321
+ success: true,
322
+ type: "pong",
323
+ timestamp: Time.now.iso8601,
324
+ session_id: @id
325
+ }
326
+ end
327
+
328
+ def execute_sql(sql, params, deadline_at: nil, cancellation: nil)
329
+ engine = @config[:engine]
330
+ raise RubyDB::ServerError, "Session has no database engine" unless engine
331
+
332
+ tokens = RubyDB::SQL::Lexer.new(sql).tokenize
333
+ statements = RubyDB::SQL::Parser.new(tokens).parse
334
+ results = statements.map do |statement|
335
+ plan = RubyDB::Execution::Planner.new(engine).plan(statement)
336
+ RubyDB::Execution::Executor.new(
337
+ engine,
338
+ deadline_at: deadline_at,
339
+ cancellation: cancellation
340
+ ).execute(plan)
341
+ end
342
+ results.size == 1 ? results.first : results
343
+ end
344
+
345
+ def resolve_permissions(username)
346
+ policy = @config[:authorization] || @config["authorization"]
347
+ return %i[read write] unless policy
348
+
349
+ users = policy[:users] || policy["users"] || {}
350
+ symbol_username = username.respond_to?(:to_sym) ? username.to_sym : username
351
+ definition = users[username] || users[username.to_s] || users[symbol_username]
352
+ permissions = definition.is_a?(Hash) ? (definition[:permissions] || definition["permissions"]) : definition
353
+ Array(permissions).map(&:to_sym)
354
+ end
355
+
356
+ def authorize_sql(sql)
357
+ required = sql.to_s.strip.split(/\s+/, 2).first.to_s.downcase
358
+ permission = %w[select show explain].include?(required) ? :read : :write
359
+ return nil if @permissions&.include?(permission)
360
+
361
+ {
362
+ success: false,
363
+ error: "Permission denied: #{permission} access is required",
364
+ timestamp: Time.now.iso8601
365
+ }
366
+ end
367
+ end
368
+ end
369
+ end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Server
5
+ # Worker - Processes requests from clients
6
+ class Worker
7
+ attr_reader :id, :stats
8
+
9
+ def initialize(id, config, engine, transaction_manager)
10
+ @id = id
11
+ @config = config
12
+ @engine = engine
13
+ @transaction_manager = transaction_manager
14
+ @queue = Queue.new
15
+ @running = false
16
+ @thread = nil
17
+ @stats = {
18
+ requests_processed: 0,
19
+ requests_failed: 0,
20
+ total_processing_time_ms: 0,
21
+ avg_processing_time_ms: 0,
22
+ last_request_time: nil,
23
+ idle_time: 0
24
+ }
25
+ @lock = Mutex.new
26
+ @last_activity = Time.now
27
+ end
28
+
29
+ def start
30
+ @lock.synchronize do
31
+ return if @running
32
+
33
+ @running = true
34
+ @thread = Thread.new { work_loop }
35
+ true
36
+ end
37
+ end
38
+
39
+ def stop
40
+ @lock.synchronize do
41
+ return unless @running
42
+
43
+ @running = false
44
+ @queue << nil
45
+
46
+ if @thread
47
+ @thread.join(5)
48
+ @thread.kill if @thread.alive?
49
+ @thread = nil
50
+ end
51
+
52
+ true
53
+ end
54
+ end
55
+
56
+ def submit(request)
57
+ @queue << request
58
+ true
59
+ end
60
+
61
+ def busy?
62
+ !@queue.empty?
63
+ end
64
+
65
+ def running?
66
+ @running
67
+ end
68
+
69
+ def stats
70
+ @lock.synchronize do
71
+ @stats.merge({
72
+ id: @id,
73
+ running: @running,
74
+ queue_size: @queue.size,
75
+ busy: busy?,
76
+ idle_time: (Time.now - @last_activity).round(2)
77
+ })
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def work_loop
84
+ while @running
85
+ begin
86
+ request = @queue.pop
87
+ break if request.nil?
88
+
89
+ process_request(request)
90
+
91
+ rescue => e
92
+ @stats[:requests_failed] += 1
93
+ end
94
+ end
95
+ end
96
+
97
+ def process_request(request)
98
+ start_time = Time.now
99
+
100
+ begin
101
+ # Process the request using the engine and transaction manager
102
+ result = handle_request(request)
103
+
104
+ # Send response back through connection
105
+ if request[:connection]
106
+ request[:connection].send_response(result)
107
+ end
108
+
109
+ @stats[:requests_processed] += 1
110
+ @last_activity = Time.now
111
+
112
+ rescue => e
113
+ @stats[:requests_failed] += 1
114
+ raise
115
+ ensure
116
+ elapsed_ms = (Time.now - start_time) * 1000
117
+ @stats[:total_processing_time_ms] += elapsed_ms
118
+ @stats[:avg_processing_time_ms] = @stats[:total_processing_time_ms] / @stats[:requests_processed] if @stats[:requests_processed] > 0
119
+ @stats[:last_request_time] = Time.now
120
+ end
121
+ end
122
+
123
+ def handle_request(request)
124
+ type = request[:type]
125
+ connection = request[:connection]
126
+
127
+ case type
128
+ when :query
129
+ handle_query(request, connection)
130
+ when :prepare
131
+ handle_prepare(request, connection)
132
+ when :execute
133
+ handle_execute(request, connection)
134
+ when :begin
135
+ handle_begin(request, connection)
136
+ when :commit
137
+ handle_commit(request, connection)
138
+ when :rollback
139
+ handle_rollback(request, connection)
140
+ when :ping
141
+ handle_ping(request, connection)
142
+ else
143
+ error_response("Unknown request type: #{type}")
144
+ end
145
+ end
146
+
147
+ def handle_query(request, connection)
148
+ sql = request[:sql]
149
+ params = request[:params] || []
150
+
151
+ # Execute query using engine
152
+ result = @engine.execute(sql, params)
153
+
154
+ success_response(result)
155
+ end
156
+
157
+ def handle_prepare(request, connection)
158
+ sql = request[:sql]
159
+ stmt_id = "stmt_#{Time.now.to_i}_#{@id}"
160
+
161
+ success_response({ statement_id: stmt_id, sql: sql })
162
+ end
163
+
164
+ def handle_execute(request, connection)
165
+ stmt_id = request[:statement_id]
166
+ params = request[:params] || []
167
+
168
+ # Execute prepared statement
169
+ result = @engine.execute_prepared(stmt_id, params)
170
+
171
+ success_response(result)
172
+ end
173
+
174
+ def handle_begin(request, connection)
175
+ @transaction_manager.begin_transaction
176
+ success_response({ transaction_id: @transaction_manager.current_transaction&.id })
177
+ end
178
+
179
+ def handle_commit(request, connection)
180
+ @transaction_manager.commit_transaction
181
+ success_response({ committed: true })
182
+ end
183
+
184
+ def handle_rollback(request, connection)
185
+ @transaction_manager.rollback_transaction
186
+ success_response({ rolled_back: true })
187
+ end
188
+
189
+ def handle_ping(request, connection)
190
+ success_response({ pong: true })
191
+ end
192
+
193
+ def success_response(data)
194
+ {
195
+ success: true,
196
+ data: data,
197
+ timestamp: Time.now.iso8601
198
+ }
199
+ end
200
+
201
+ def error_response(message)
202
+ {
203
+ success: false,
204
+ error: message,
205
+ timestamp: Time.now.iso8601
206
+ }
207
+ end
208
+ end
209
+ end
210
+ end