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,461 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module RubyDB
6
+ module Storage
7
+ # Tuple - Binary representation of a row for storage with proper variable-length support
8
+ class Tuple
9
+ attr_reader :data, :header, :column_count, :columns, :values, :size
10
+
11
+ # Header format:
12
+ # [version(1)] [flags(1)] [null_bitmap(4)] [column_count(2)] [tuple_size(4)]
13
+ # [column_offsets...] (2 bytes each)
14
+ HEADER_SIZE = 12 # Version(1) + Flags(1) + NullBitmap(4) + ColumnCount(2) + TupleSize(4)
15
+ MAX_COLUMNS = 65535
16
+ MAX_TUPLE_SIZE = 65535 # 64KB max tuple size
17
+
18
+ def initialize(columns, values = nil)
19
+ @columns = columns
20
+ @values = values ? values.dup : {}
21
+ @header = nil
22
+ @data = nil
23
+ @dirty = true
24
+ @size = 0
25
+ @null_bitmap = 0
26
+ @column_offsets = []
27
+ @column_lengths = []
28
+ @flags = 0
29
+ @version = 1
30
+ @is_compressed = false
31
+ @compression_threshold = 1024 # Compress if data > 1KB
32
+
33
+ # Validate columns
34
+ raise ArgumentError, "Columns cannot be empty" if @columns.empty?
35
+ raise ArgumentError, "Too many columns (max #{MAX_COLUMNS})" if @columns.size > MAX_COLUMNS
36
+ end
37
+
38
+ def serialize
39
+ return @data unless @dirty
40
+
41
+ # Calculate null bitmap
42
+ calculate_null_bitmap
43
+
44
+ # Serialize each column value
45
+ column_data = []
46
+ @column_offsets = []
47
+ @column_lengths = []
48
+ @size = HEADER_SIZE
49
+ total_data = StringIO.new("".b)
50
+
51
+ @columns.each_with_index do |col, idx|
52
+ value = @values[col.name]
53
+
54
+ # Check if value is NULL
55
+ is_null = value.nil?
56
+
57
+ # Serialize the value
58
+ serialized = if is_null
59
+ "".b
60
+ else
61
+ serialize_value(col, value)
62
+ end
63
+
64
+ # Store offset and length
65
+ @column_offsets[idx] = @size
66
+ @column_lengths[idx] = serialized.bytesize
67
+
68
+ # Write data
69
+ total_data.write(serialized)
70
+ @size += serialized.bytesize
71
+ end
72
+
73
+ # Build header
74
+ header_data = build_header
75
+
76
+ # Combine header and data
77
+ @data = header_data + total_data.string
78
+
79
+ # Check if compression is needed
80
+ if @data.bytesize > @compression_threshold
81
+ compressed = compress_data(@data)
82
+ if compressed.bytesize < @data.bytesize
83
+ @data = compressed
84
+ @flags |= 0x01 # Set compression flag
85
+ @is_compressed = true
86
+ end
87
+ end
88
+
89
+ @dirty = false
90
+ @data
91
+ end
92
+
93
+ def deserialize(data)
94
+ @data = data
95
+ @is_compressed = false
96
+
97
+ # Check if data is compressed
98
+ if data.bytesize >= 1
99
+ # Peek at flags
100
+ flags_byte = data[1].unpack("C").first if data.bytesize > 1
101
+ if flags_byte && (flags_byte & 0x01) != 0
102
+ @data = decompress_data(data)
103
+ @is_compressed = true
104
+ end
105
+ end
106
+
107
+ parse_header(@data)
108
+ parse_column_data(@data)
109
+
110
+ @dirty = false
111
+ self
112
+ rescue => e
113
+ raise CorruptionError, "Failed to deserialize tuple: #{e.message}"
114
+ end
115
+
116
+ def update_value(column_name, value)
117
+ @values[column_name] = value
118
+ @dirty = true
119
+ end
120
+
121
+ def update_values(new_values)
122
+ new_values.each do |name, value|
123
+ @values[name] = value
124
+ end
125
+ @dirty = true
126
+ end
127
+
128
+ def get_value(column_name)
129
+ @values[column_name]
130
+ end
131
+
132
+ def to_row(row_id)
133
+ Row.new(row_id, @columns, @values)
134
+ end
135
+
136
+ def to_hash
137
+ @values.dup
138
+ end
139
+
140
+ def to_ary
141
+ @columns.map { |col| @values[col.name] }
142
+ end
143
+
144
+ def compressed?
145
+ @is_compressed
146
+ end
147
+
148
+ def size
149
+ @data ? @data.bytesize : @size
150
+ end
151
+
152
+ def column_count
153
+ @columns.size
154
+ end
155
+
156
+ def null?(column_name)
157
+ @values[column_name].nil?
158
+ end
159
+
160
+ def each_column
161
+ @columns.each do |col|
162
+ yield col, @values[col.name]
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def build_header
169
+ header_data = "".b
170
+
171
+ # Version (1 byte)
172
+ header_data << [@version].pack("C")
173
+
174
+ # Flags (1 byte)
175
+ # Bit 0: compressed
176
+ # Bit 1: has nulls
177
+ # Bit 2: has defaults
178
+ flags = @flags
179
+ flags |= 0x02 if has_nulls?
180
+ flags |= 0x04 if has_defaults?
181
+ header_data << [flags].pack("C")
182
+
183
+ # Null bitmap (4 bytes)
184
+ header_data << [@null_bitmap].pack("L")
185
+
186
+ # Column count (2 bytes)
187
+ header_data << [@columns.size].pack("S")
188
+
189
+ # Tuple size (4 bytes)
190
+ header_data << [@size].pack("L")
191
+
192
+ # Column offsets (2 bytes each)
193
+ @column_offsets.each do |offset|
194
+ header_data << [offset].pack("S")
195
+ end
196
+
197
+ header_data
198
+ end
199
+
200
+ def parse_header(data)
201
+ offset = 0
202
+
203
+ # Version
204
+ @version = data[offset].unpack("C").first
205
+ offset += 1
206
+
207
+ # Flags
208
+ @flags = data[offset].unpack("C").first
209
+ offset += 1
210
+
211
+ # Null bitmap
212
+ @null_bitmap = data[offset, 4].unpack("L").first
213
+ offset += 4
214
+
215
+ # Column count
216
+ @column_count = data[offset, 2].unpack("S").first
217
+ offset += 2
218
+
219
+ # Tuple size
220
+ @size = data[offset, 4].unpack("L").first
221
+ offset += 4
222
+
223
+ # Column offsets
224
+ @column_offsets = []
225
+ @column_lengths = []
226
+
227
+ @column_count.times do
228
+ @column_offsets << data[offset, 2].unpack("S").first
229
+ offset += 2
230
+ end
231
+
232
+ # Calculate column lengths
233
+ @column_count.times do |i|
234
+ if i < @column_count - 1
235
+ @column_lengths[i] = @column_offsets[i + 1] - @column_offsets[i]
236
+ else
237
+ @column_lengths[i] = @size - @column_offsets[i]
238
+ end
239
+ end
240
+ end
241
+
242
+ def parse_column_data(data)
243
+ @values = {}
244
+
245
+ @column_count.times do |i|
246
+ col = @columns[i]
247
+ offset = @column_offsets[i]
248
+ length = @column_lengths[i]
249
+
250
+ # Check if value is NULL
251
+ if null_at?(i)
252
+ @values[col.name] = nil
253
+ next
254
+ end
255
+
256
+ # Read and deserialize value
257
+ value_data = data[offset, length]
258
+ @values[col.name] = deserialize_value(col, value_data)
259
+ end
260
+ end
261
+
262
+ def serialize_value(column, value)
263
+ type_class = column.type_class
264
+
265
+ # Handle special types
266
+ case type_class
267
+ when :text, :varchar, :char
268
+ value = value.to_s.encode("UTF-8")
269
+ length_prefix = [value.bytesize].pack("S")
270
+ length_prefix + value
271
+
272
+ when :blob
273
+ value = value.is_a?(String) ? value.b : value.to_s.b
274
+ length_prefix = [value.bytesize].pack("L")
275
+ length_prefix + value
276
+
277
+ when :json
278
+ value = value.is_a?(Hash) || value.is_a?(Array) ? value : {}
279
+ json_str = JSON.generate(value)
280
+ length_prefix = [json_str.bytesize].pack("S")
281
+ length_prefix + json_str
282
+
283
+ when :uuid
284
+ # UUID is stored as 16 bytes binary
285
+ if value.nil?
286
+ "\x00" * 16
287
+ else
288
+ str = value.to_s.gsub("-", "")
289
+ [str].pack("H*")
290
+ end
291
+
292
+ when :decimal
293
+ require "bigdecimal"
294
+ bd = value.is_a?(BigDecimal) ? value : BigDecimal(value.to_s)
295
+ str = bd.to_s("F")
296
+ length_prefix = [str.bytesize].pack("S")
297
+ length_prefix + str
298
+
299
+ when :date
300
+ if value.nil?
301
+ [0].pack("L")
302
+ else
303
+ days = value - Date.new(1970, 1, 1)
304
+ [days.to_i].pack("L")
305
+ end
306
+
307
+ when :time
308
+ if value.nil?
309
+ [0].pack("Q")
310
+ else
311
+ seconds = value.hour * 3600 + value.min * 60 + value.sec
312
+ microseconds = value.usec
313
+ total = seconds * 1_000_000 + microseconds
314
+ [total].pack("Q")
315
+ end
316
+
317
+ when :timestamp
318
+ if value.nil?
319
+ [0].pack("Q")
320
+ else
321
+ [value.to_i].pack("Q")
322
+ end
323
+
324
+ when :integer
325
+ [value.to_i].pack("l>")
326
+
327
+ when :bigint
328
+ [value.to_i].pack("q>")
329
+
330
+ when :smallint
331
+ [value.to_i].pack("s>")
332
+
333
+ when :float
334
+ [value.to_f].pack("E")
335
+
336
+ when :boolean
337
+ value ? "\x01".b : "\x00".b
338
+
339
+ else
340
+ # Unknown type - use string representation
341
+ str = value.to_s
342
+ length_prefix = [str.bytesize].pack("S")
343
+ length_prefix + str
344
+ end
345
+ end
346
+
347
+ def deserialize_value(column, data)
348
+ return nil if data.nil? || data.empty?
349
+
350
+ type_class = column.type_class
351
+
352
+ case type_class
353
+ when :text, :varchar, :char
354
+ data.force_encoding("UTF-8")
355
+
356
+ when :blob
357
+ data.b
358
+
359
+ when :json
360
+ begin
361
+ JSON.parse(data.force_encoding("UTF-8"))
362
+ rescue JSON::ParserError
363
+ {}
364
+ end
365
+
366
+ when :uuid
367
+ hex = data.unpack("H*").first
368
+ "#{hex[0...8]}-#{hex[8...12]}-#{hex[12...16]}-#{hex[16...20]}-#{hex[20...32]}"
369
+
370
+ when :decimal
371
+ require "bigdecimal"
372
+ BigDecimal(data.force_encoding("UTF-8"))
373
+
374
+ when :date
375
+ require "date"
376
+ days = data.unpack("L").first
377
+ Date.new(1970, 1, 1) + days
378
+
379
+ when :time
380
+ require "time"
381
+ total = data.unpack("Q").first
382
+ seconds = total / 1_000_000
383
+ microseconds = total % 1_000_000
384
+ hour = seconds / 3600
385
+ minute = (seconds % 3600) / 60
386
+ sec = seconds % 60
387
+ Time.new(1970, 1, 1, hour, minute, sec, microseconds)
388
+
389
+ when :timestamp
390
+ require "time"
391
+ Time.at(data.unpack("Q").first)
392
+
393
+ when :integer
394
+ data.unpack("l>").first
395
+
396
+ when :bigint
397
+ data.unpack("q>").first
398
+
399
+ when :smallint
400
+ data.unpack("s>").first
401
+
402
+ when :float
403
+ data.unpack("E").first
404
+
405
+ when :boolean
406
+ data.unpack("C").first == 1
407
+
408
+ else
409
+ data.force_encoding("UTF-8")
410
+ end
411
+ end
412
+
413
+ def calculate_null_bitmap
414
+ @null_bitmap = 0
415
+ @columns.each_with_index do |col, idx|
416
+ if @values[col.name].nil?
417
+ @null_bitmap |= (1 << idx)
418
+ end
419
+ end
420
+ end
421
+
422
+ def null_at?(index)
423
+ (@null_bitmap & (1 << index)) != 0
424
+ end
425
+
426
+ def has_nulls?
427
+ @null_bitmap != 0
428
+ end
429
+
430
+ def has_defaults?
431
+ @columns.any? { |col| col.has_default? && @values[col.name].nil? }
432
+ end
433
+
434
+ def compress_data(data)
435
+ return data if data.bytesize < 100
436
+
437
+ begin
438
+ require "zlib"
439
+ Zlib::Deflate.deflate(data, Zlib::BEST_SPEED)
440
+ rescue LoadError
441
+ data
442
+ end
443
+ end
444
+
445
+ def decompress_data(data)
446
+ begin
447
+ require "zlib"
448
+ Zlib::Inflate.inflate(data)
449
+ rescue LoadError, Zlib::DataError
450
+ data # Return original if decompression fails
451
+ end
452
+ end
453
+
454
+ def validate_tuple_size
455
+ if @size > MAX_TUPLE_SIZE
456
+ raise StorageError, "Tuple size #{@size} exceeds maximum #{MAX_TUPLE_SIZE}"
457
+ end
458
+ end
459
+ end
460
+ end
461
+ end