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,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module RubyDB
6
+ module Types
7
+ # JSON type (stores JSON data)
8
+ class Json < Type
9
+ def initialize
10
+ super(:json)
11
+ end
12
+
13
+ def serialize(value)
14
+ validate(value)
15
+ return "null".encode("UTF-8") if value.nil?
16
+ JSON.generate(value)
17
+ end
18
+
19
+ def deserialize(bytes)
20
+ return nil if bytes.nil? || bytes.empty?
21
+ JSON.parse(bytes.force_encoding("UTF-8"))
22
+ rescue JSON::ParserError
23
+ nil
24
+ end
25
+
26
+ def validate(value)
27
+ return true if value.nil?
28
+ raise ConstraintError, "Invalid JSON: #{value}" unless value.is_a?(Hash) || value.is_a?(Array)
29
+ true
30
+ end
31
+
32
+ def default
33
+ {}
34
+ end
35
+
36
+ def storage_size
37
+ nil # Variable length
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Types
5
+ # NULL type (represents absence of value)
6
+ class Null < Type
7
+ SIZE = 0
8
+
9
+ def initialize
10
+ super(:null, size: SIZE)
11
+ end
12
+
13
+ def serialize(value)
14
+ ""
15
+ end
16
+
17
+ def deserialize(bytes)
18
+ nil
19
+ end
20
+
21
+ def validate(value)
22
+ true # Always valid
23
+ end
24
+
25
+ def default
26
+ nil
27
+ end
28
+
29
+ def storage_size
30
+ 0
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Types
5
+ # 16-bit signed integer
6
+ class SmallInt < Type
7
+ RANGE = (-32_768..32_767).freeze
8
+ SIZE = 2
9
+
10
+ def initialize
11
+ super(:smallint, size: SIZE)
12
+ end
13
+
14
+ def serialize(value)
15
+ validate(value)
16
+ [value.to_i].pack("s>") # Big-endian 16-bit signed
17
+ end
18
+
19
+ def deserialize(bytes)
20
+ return nil if bytes.nil? || bytes.empty?
21
+ bytes.unpack("s>").first
22
+ end
23
+
24
+ def validate(value)
25
+ return true if value.nil?
26
+ raise ConstraintError, "Invalid smallint: #{value}" unless value.is_a?(::Integer)
27
+ raise ConstraintError, "SmallInt out of range: #{value}" unless RANGE.include?(value)
28
+ true
29
+ end
30
+
31
+ def default
32
+ 0
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Types
5
+ # Variable-length text string (unlimited)
6
+ class Text < Type
7
+ def initialize
8
+ super(:text)
9
+ end
10
+
11
+ def serialize(value)
12
+ validate(value)
13
+ return "" if value.nil?
14
+ value.to_s.encode("UTF-8")
15
+ end
16
+
17
+ def deserialize(bytes)
18
+ return nil if bytes.nil? || bytes.empty?
19
+ bytes.force_encoding("UTF-8")
20
+ end
21
+
22
+ def validate(value)
23
+ return true if value.nil?
24
+ raise ConstraintError, "Invalid text: #{value}" unless value.respond_to?(:to_s)
25
+ true
26
+ end
27
+
28
+ def default
29
+ ""
30
+ end
31
+
32
+ def storage_size
33
+ nil # Variable length
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module RubyDB
6
+ module Types
7
+ # Time type (hour, minute, second)
8
+ class Time < Type
9
+ SIZE = 8 # Microseconds since midnight
10
+
11
+ def initialize
12
+ super(:time, size: SIZE)
13
+ end
14
+
15
+ def serialize(value)
16
+ validate(value)
17
+ return "\x00" * SIZE if value.nil?
18
+ seconds = value.hour * 3600 + value.min * 60 + value.sec
19
+ microseconds = value.usec
20
+ total = seconds * 1_000_000 + microseconds
21
+ [total].pack("q>")
22
+ end
23
+
24
+ def deserialize(bytes)
25
+ return nil if bytes.nil? || bytes.empty?
26
+ total = bytes.unpack("q>").first
27
+ seconds = total / 1_000_000
28
+ microseconds = total % 1_000_000
29
+ hour = seconds / 3600
30
+ minute = (seconds % 3600) / 60
31
+ sec = seconds % 60
32
+ ::Time.new(1970, 1, 1, hour, minute, sec, microseconds)
33
+ end
34
+
35
+ def validate(value)
36
+ return true if value.nil?
37
+ raise ConstraintError, "Invalid time: #{value}" unless value.is_a?(::Time) || value.is_a?(::DateTime)
38
+ true
39
+ end
40
+
41
+ def default
42
+ ::Time.now
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module RubyDB
6
+ module Types
7
+ # Timestamp (date + time with timezone)
8
+ class Timestamp < Type
9
+ SIZE = 8 # Seconds since epoch (Unix timestamp)
10
+
11
+ def initialize
12
+ super(:timestamp, size: SIZE)
13
+ end
14
+
15
+ def serialize(value)
16
+ value = ::Time.parse(value) if value.is_a?(String)
17
+ validate(value)
18
+ return "\x00" * SIZE if value.nil?
19
+ [value.to_i].pack("q>")
20
+ end
21
+
22
+ def deserialize(bytes)
23
+ return nil if bytes.nil? || bytes.empty?
24
+ seconds = bytes.unpack("q>").first
25
+ ::Time.at(seconds)
26
+ end
27
+
28
+ def validate(value)
29
+ return true if value.nil?
30
+ raise ConstraintError, "Invalid timestamp: #{value}" unless value.is_a?(::Time) || value.is_a?(::DateTime) || value.is_a?(::Date)
31
+ true
32
+ end
33
+
34
+ def default
35
+ ::Time.now
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../errors/constraint_error"
4
+ require_relative "../errors/configuration_error"
5
+
6
+ module RubyDB
7
+ module Types
8
+ # Base class for all data types
9
+ class Type
10
+ attr_reader :name, :size, :precision, :scale
11
+
12
+ def initialize(name, size: nil, precision: nil, scale: nil)
13
+ @name = name
14
+ @size = size
15
+ @precision = precision
16
+ @scale = scale
17
+ end
18
+
19
+ def serialize(value)
20
+ raise NotImplementedError, "#{self.class} must implement #serialize"
21
+ end
22
+
23
+ def deserialize(bytes)
24
+ raise NotImplementedError, "#{self.class} must implement #deserialize"
25
+ end
26
+
27
+ def validate(value)
28
+ raise NotImplementedError, "#{self.class} must implement #validate"
29
+ end
30
+
31
+ def compare(a, b)
32
+ return 0 if a == b
33
+ return -1 if a < b
34
+ 1
35
+ end
36
+
37
+ def default
38
+ nil
39
+ end
40
+
41
+ def sql_type
42
+ @name.to_s.upcase
43
+ end
44
+
45
+ def storage_size
46
+ @size
47
+ end
48
+
49
+ def to_s
50
+ sql_type
51
+ end
52
+
53
+ def inspect
54
+ "#<#{self.class} name=#{@name} size=#{@size}>"
55
+ end
56
+ end
57
+
58
+ # Type registry for looking up types by name
59
+ class TypeRegistry
60
+ @types = {}
61
+
62
+ class << self
63
+ attr_reader :types
64
+
65
+ def register(name, type_class = nil)
66
+ klass = type_class || resolve(name)
67
+ return if klass.nil?
68
+
69
+ @types[name.to_sym] = klass
70
+ end
71
+
72
+ def lookup(name, **kwargs)
73
+ type_class = @types[name.to_sym] || resolve(name)
74
+ if type_class
75
+ return type_class.new(kwargs.fetch(:limit, 255)) if type_class == Varchar
76
+
77
+ return type_class.new(**kwargs)
78
+ end
79
+
80
+ raise ConfigurationError, "Unknown type: #{name}"
81
+ end
82
+
83
+ def reset
84
+ @types = {}
85
+ end
86
+
87
+ private
88
+
89
+ def resolve(name)
90
+ name = name.to_sym
91
+ const_name = case name
92
+ when :bigint then "BigInt"
93
+ when :smallint then "SmallInt"
94
+ when :float then "Float"
95
+ when :decimal then "Decimal"
96
+ when :boolean then "Boolean"
97
+ when :text then "Text"
98
+ when :varchar then "Varchar"
99
+ when :blob then "Blob"
100
+ when :date then "Date"
101
+ when :time then "Time"
102
+ when :timestamp then "Timestamp"
103
+ when :json then "Json"
104
+ when :uuid then "UUID"
105
+ when :null then "Null"
106
+ when :integer then "Integer"
107
+ else name.to_s.split("_").map { |part| part.capitalize }.join
108
+ end
109
+
110
+ klass = RubyDB::Types.const_get(const_name)
111
+ @types[name] = klass
112
+ klass
113
+ rescue NameError
114
+ nil
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module RubyDB
6
+ module Types
7
+ # UUID type (Universally Unique Identifier)
8
+ class UUID < Type
9
+ SIZE = 16
10
+ UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
11
+
12
+ def initialize
13
+ super(:uuid, size: SIZE)
14
+ end
15
+
16
+ def serialize(value)
17
+ validate(value)
18
+ return "\x00" * SIZE if value.nil?
19
+ # Remove hyphens and convert hex to bytes
20
+ hex = value.to_s.gsub("-", "")
21
+ [hex].pack("H*")
22
+ end
23
+
24
+ def deserialize(bytes)
25
+ return nil if bytes.nil? || bytes.empty?
26
+ hex = bytes.unpack("H*").first
27
+ # Add hyphens back
28
+ "#{hex[0...8]}-#{hex[8...12]}-#{hex[12...16]}-#{hex[16...20]}-#{hex[20...32]}"
29
+ end
30
+
31
+ def validate(value)
32
+ return true if value.nil?
33
+ str = value.to_s
34
+ raise ConstraintError, "Invalid UUID: #{value}" unless str.match?(UUID_REGEX)
35
+ true
36
+ end
37
+
38
+ def default
39
+ SecureRandom.uuid
40
+ end
41
+
42
+ def generate
43
+ SecureRandom.uuid
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Types
5
+ # Variable-length text string with maximum length
6
+ class Varchar < Text
7
+ attr_reader :limit
8
+
9
+ def initialize(limit)
10
+ super()
11
+ @name = :varchar
12
+ @limit = limit
13
+ @size = limit
14
+ end
15
+
16
+ def serialize(value)
17
+ validate(value)
18
+ return "" if value.nil?
19
+ str = value.to_s.encode("UTF-8")
20
+ raise ConstraintError, "VARCHAR exceeds limit: #{str.length} > #{@limit}" if str.length > @limit
21
+ str
22
+ end
23
+
24
+ def validate(value)
25
+ return true if value.nil?
26
+ raise ConstraintError, "Invalid varchar: #{value}" unless value.respond_to?(:to_s)
27
+ str = value.to_s
28
+ raise ConstraintError, "VARCHAR exceeds limit: #{str.length} > #{@limit}" if str.length > @limit
29
+ true
30
+ end
31
+
32
+ def to_s
33
+ "VARCHAR(#{@limit})"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RubyDB version information
4
+ # Follows Semantic Versioning (SemVer) 2.0.0
5
+ # See: https://semver.org/
6
+
7
+ module RubyDB
8
+ # Current version of RubyDB
9
+ #
10
+ # Format: MAJOR.MINOR.PATCH
11
+ # - MAJOR: Incompatible API changes
12
+ # - MINOR: Backwards-compatible new functionality
13
+ # - PATCH: Backwards-compatible bug fixes
14
+ VERSION = "0.1.0"
15
+
16
+ # Version components for easy access
17
+ module Version
18
+ MAJOR = 0
19
+ MINOR = 1
20
+ PATCH = 0
21
+ PRE = nil # e.g., "alpha", "beta", "rc1"
22
+
23
+ def self.to_s
24
+ [MAJOR, MINOR, PATCH].join(".")
25
+ end
26
+
27
+ def self.full
28
+ version = to_s
29
+ version += "-#{PRE}" if PRE
30
+ version
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "zlib"
5
+ require "time"
6
+
7
+ module RubyDB
8
+ module WAL
9
+ # Archive - Manages WAL archiving and restoration
10
+ class Archive
11
+ attr_reader :stats
12
+
13
+ def initialize(wal_dir, archive_dir = nil, config = {})
14
+ @wal_dir = wal_dir
15
+ @archive_dir = archive_dir || File.join(wal_dir, "archive")
16
+ @config = config
17
+ @compression = config[:compression] || true
18
+ @max_archive_size = config[:max_size] || 10 * 1024 * 1024 * 1024 # 10GB
19
+ @retention_days = config[:retention_days] || 30
20
+ @stats = {
21
+ archives_created: 0,
22
+ archives_restored: 0,
23
+ archives_deleted: 0,
24
+ total_archive_size: 0,
25
+ compressed_bytes: 0,
26
+ uncompressed_bytes: 0,
27
+ archive_failures: 0
28
+ }
29
+ @lock = Mutex.new
30
+
31
+ # Create archive directory
32
+ FileUtils.mkdir_p(@archive_dir)
33
+ end
34
+
35
+ def archive_segment(segment)
36
+ @lock.synchronize do
37
+ start_time = Time.now
38
+
39
+ begin
40
+ # Check if segment exists
41
+ unless segment.exists?
42
+ return false
43
+ end
44
+
45
+ # Archive the segment
46
+ archive_path = segment.archive
47
+ archive_size = File.size(archive_path)
48
+
49
+ # Compress if enabled
50
+ if @compression && archive_size > 0
51
+ compressed_path = "#{archive_path}.gz"
52
+ compress_file(archive_path, compressed_path)
53
+ File.delete(archive_path)
54
+ archive_path = compressed_path
55
+ archive_size = File.size(archive_path)
56
+ @stats[:compressed_bytes] += archive_size
57
+ end
58
+
59
+ @stats[:archives_created] += 1
60
+ @stats[:total_archive_size] += archive_size
61
+ @stats[:uncompressed_bytes] += archive_size
62
+
63
+ # Clean old archives if needed
64
+ clean_old_archives
65
+
66
+ { path: archive_path, size: archive_size }
67
+
68
+ rescue => e
69
+ @stats[:archive_failures] += 1
70
+ raise
71
+ end
72
+ end
73
+ end
74
+
75
+ def restore_archive(segment_id)
76
+ @lock.synchronize do
77
+ # Find archive file
78
+ archive_path = File.join(@archive_dir, "wal_#{segment_id.to_s.rjust(8, '0')}.log")
79
+
80
+ unless File.exist?(archive_path)
81
+ # Try compressed
82
+ compressed_path = "#{archive_path}.gz"
83
+ if File.exist?(compressed_path)
84
+ archive_path = compressed_path
85
+ else
86
+ return false
87
+ end
88
+ end
89
+
90
+ # Decompress if needed
91
+ if archive_path.end_with?(".gz")
92
+ decompressed_path = archive_path.gsub(/\.gz$/, "")
93
+ decompress_file(archive_path, decompressed_path)
94
+ archive_path = decompressed_path
95
+ end
96
+
97
+ # Copy to WAL directory
98
+ wal_path = File.join(@wal_dir, "wal_#{segment_id.to_s.rjust(8, '0')}.log")
99
+ FileUtils.cp(archive_path, wal_path)
100
+
101
+ @stats[:archives_restored] += 1
102
+ wal_path
103
+ end
104
+ end
105
+
106
+ def list_archives
107
+ @lock.synchronize do
108
+ archives = []
109
+
110
+ Dir.glob(File.join(@archive_dir, "wal_*.log*")).each do |path|
111
+ if path =~ /wal_(\d+)\.log(?:\.gz)?$/
112
+ segment_id = $1.to_i
113
+ size = File.size(path)
114
+ modified = File.mtime(path)
115
+
116
+ archives << {
117
+ segment_id: segment_id,
118
+ path: path,
119
+ size: size,
120
+ modified: modified,
121
+ compressed: path.end_with?(".gz")
122
+ }
123
+ end
124
+ end
125
+
126
+ archives.sort_by { |a| a[:segment_id] }
127
+ end
128
+ end
129
+
130
+ def clean_old_archives
131
+ @lock.synchronize do
132
+ # Check total size
133
+ archives = list_archives
134
+ total_size = archives.sum { |a| a[:size] }
135
+
136
+ # If over limit, remove oldest
137
+ if total_size > @max_archive_size
138
+ archives.sort_by! { |a| a[:modified] }
139
+ archives.each do |archive|
140
+ break if total_size <= @max_archive_size
141
+
142
+ File.delete(archive[:path])
143
+ total_size -= archive[:size]
144
+ @stats[:archives_deleted] += 1
145
+ end
146
+ end
147
+
148
+ # Remove old archives
149
+ cutoff = Time.now - @retention_days * 24 * 60 * 60
150
+ archives.each do |archive|
151
+ if archive[:modified] < cutoff
152
+ File.delete(archive[:path])
153
+ @stats[:archives_deleted] += 1
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ def stats
160
+ @lock.synchronize do
161
+ archives = list_archives
162
+ total_size = archives.sum { |a| a[:size] }
163
+
164
+ @stats.merge({
165
+ archive_count: archives.size,
166
+ total_archive_size: total_size,
167
+ retention_days: @retention_days,
168
+ max_archive_size: @max_archive_size,
169
+ compression: @compression
170
+ })
171
+ end
172
+ end
173
+
174
+ private
175
+
176
+ def compress_file(source, destination)
177
+ File.open(source, "rb") do |input|
178
+ Zlib::GzipWriter.open(destination) do |output|
179
+ output.write(input.read)
180
+ end
181
+ end
182
+ end
183
+
184
+ def decompress_file(source, destination)
185
+ Zlib::GzipReader.open(source) do |input|
186
+ File.open(destination, "wb") do |output|
187
+ output.write(input.read)
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end