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,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Constraints
5
+ # NotNullConstraint - Ensures column is not NULL
6
+ class NotNullConstraint < Constraint
7
+ attr_reader :column
8
+
9
+ def initialize(table_name, column, options = {})
10
+ super(
11
+ options[:name] || "nn_#{table_name}_#{column}",
12
+ TYPE_NOT_NULL,
13
+ table_name,
14
+ options
15
+ )
16
+ @column = column
17
+ @validated = false
18
+ end
19
+
20
+ def validate(row)
21
+ return true unless enabled?
22
+
23
+ if row[@column].nil?
24
+ @validation_errors << "Column '#{@column}' cannot be NULL"
25
+ return false
26
+ end
27
+
28
+ true
29
+ end
30
+
31
+ def validate_batch(rows)
32
+ results = { valid: [], invalid: [] }
33
+
34
+ rows.each do |row|
35
+ if validate(row)
36
+ results[:valid] << row
37
+ else
38
+ results[:invalid] << row
39
+ end
40
+ end
41
+
42
+ results
43
+ end
44
+
45
+ def find_null_rows(rows)
46
+ rows.select { |row| row[@column].nil? }
47
+ end
48
+
49
+ def to_sql
50
+ "#{@column} NOT NULL"
51
+ end
52
+
53
+ def to_hash
54
+ super.merge({
55
+ column: @column
56
+ })
57
+ end
58
+
59
+ def inspect
60
+ "#<NotNullConstraint name=#{@name} column=#{@column}>"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Constraints
5
+ # PrimaryKeyConstraint - Ensures unique identification of rows
6
+ class PrimaryKeyConstraint < Constraint
7
+ attr_reader :columns
8
+
9
+ def initialize(table_name, columns, options = {})
10
+ super(
11
+ options[:name] || "pk_#{table_name}",
12
+ TYPE_PRIMARY_KEY,
13
+ table_name,
14
+ options
15
+ )
16
+ @columns = columns.is_a?(Array) ? columns : [columns]
17
+ @auto_increment = options[:auto_increment] || false
18
+ @sequence_name = options[:sequence_name] || "seq_#{table_name}_#{@columns.first}"
19
+ @validated = false
20
+ end
21
+
22
+ def validate(row)
23
+ return true unless enabled?
24
+
25
+ # Check all columns are present
26
+ @columns.each do |col|
27
+ if row[col].nil?
28
+ @validation_errors << "Column '#{col}' cannot be NULL"
29
+ return false
30
+ end
31
+ end
32
+
33
+ # Check uniqueness
34
+ key = @columns.map { |col| row[col] }.join("||")
35
+ if @existing_keys && @existing_keys.include?(key)
36
+ @validation_errors << "Duplicate primary key: #{key}"
37
+ return false
38
+ end
39
+
40
+ true
41
+ end
42
+
43
+ def validate_batch(rows)
44
+ @existing_keys = Set.new
45
+ results = { valid: [], invalid: [] }
46
+
47
+ rows.each do |row|
48
+ # Check all columns are present
49
+ all_present = true
50
+ @columns.each do |col|
51
+ if row[col].nil?
52
+ all_present = false
53
+ @validation_errors << "Column '#{col}' cannot be NULL"
54
+ end
55
+ end
56
+
57
+ if all_present
58
+ key = @columns.map { |col| row[col] }.join("||")
59
+ if @existing_keys.include?(key)
60
+ @validation_errors << "Duplicate primary key: #{key}"
61
+ results[:invalid] << row
62
+ else
63
+ @existing_keys.add(key)
64
+ results[:valid] << row
65
+ end
66
+ else
67
+ results[:invalid] << row
68
+ end
69
+ end
70
+
71
+ results
72
+ end
73
+
74
+ def generate_next_id(current_max = 0)
75
+ return nil unless @auto_increment
76
+ current_max + 1
77
+ end
78
+
79
+ def to_sql
80
+ cols = @columns.join(", ")
81
+ sql = "PRIMARY KEY (#{cols})"
82
+ sql << " AUTO_INCREMENT" if @auto_increment
83
+ sql
84
+ end
85
+
86
+ def to_hash
87
+ super.merge({
88
+ columns: @columns,
89
+ auto_increment: @auto_increment,
90
+ sequence_name: @sequence_name
91
+ })
92
+ end
93
+
94
+ def inspect
95
+ "#<PrimaryKeyConstraint name=#{@name} columns=#{@columns.join(', ')}>"
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module RubyDB
6
+ module Constraints
7
+ # UniqueConstraint - Ensures unique values in columns
8
+ class UniqueConstraint < Constraint
9
+ attr_reader :columns
10
+
11
+ def initialize(table_name, columns, options = {})
12
+ super(
13
+ options[:name] || "uniq_#{table_name}_#{columns.join('_')}",
14
+ TYPE_UNIQUE,
15
+ table_name,
16
+ options
17
+ )
18
+ @columns = columns.is_a?(Array) ? columns : [columns]
19
+ @nulls_distinct = options[:nulls_distinct] != false
20
+ @validated = false
21
+ @existing_keys = Set.new
22
+ end
23
+
24
+ def validate(row)
25
+ return true unless enabled?
26
+
27
+ # Build key from column values
28
+ key = build_key(row)
29
+
30
+ # Check if key already exists (duplicate)
31
+ if @existing_keys.include?(key)
32
+ @validation_errors << "Duplicate value for unique constraint: #{key}"
33
+ return false
34
+ end
35
+
36
+ @existing_keys.add(key)
37
+ true
38
+ end
39
+
40
+ def validate_batch(rows)
41
+ @existing_keys = Set.new
42
+ results = { valid: [], invalid: [] }
43
+
44
+ rows.each do |row|
45
+ key = build_key(row)
46
+
47
+ # Skip if all values are NULL and nulls_distinct is true
48
+ if @nulls_distinct && all_null?(row)
49
+ results[:valid] << row
50
+ next
51
+ end
52
+
53
+ if @existing_keys.include?(key)
54
+ @validation_errors << "Duplicate value for unique constraint: #{key}"
55
+ results[:invalid] << row
56
+ else
57
+ @existing_keys.add(key)
58
+ results[:valid] << row
59
+ end
60
+ end
61
+
62
+ results
63
+ end
64
+
65
+ def build_key(row)
66
+ @columns.map { |col| row[col] }.join("||")
67
+ end
68
+
69
+ def all_null?(row)
70
+ @columns.all? { |col| row[col].nil? }
71
+ end
72
+
73
+ def check_duplicates(rows)
74
+ seen = Set.new
75
+ duplicates = []
76
+
77
+ rows.each do |row|
78
+ key = build_key(row)
79
+ if seen.include?(key)
80
+ duplicates << row
81
+ else
82
+ seen.add(key)
83
+ end
84
+ end
85
+
86
+ duplicates
87
+ end
88
+
89
+ def to_sql
90
+ cols = @columns.join(", ")
91
+ sql = "UNIQUE (#{cols})"
92
+ sql << " NULLS NOT DISTINCT" unless @nulls_distinct
93
+ sql
94
+ end
95
+
96
+ def to_hash
97
+ super.merge({
98
+ columns: @columns,
99
+ nulls_distinct: @nulls_distinct
100
+ })
101
+ end
102
+
103
+ def inspect
104
+ "#<UniqueConstraint name=#{@name} columns=#{@columns.join(', ')}>"
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,350 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "monitor"
5
+
6
+ module RubyDB
7
+ module Constraints
8
+ # Validator - Validates rows against all constraints
9
+ class Validator
10
+ attr_reader :constraints, :stats
11
+
12
+ def initialize
13
+ @constraints = {
14
+ primary_key: [],
15
+ foreign_key: [],
16
+ unique: [],
17
+ not_null: [],
18
+ check: []
19
+ }
20
+ @stats = {
21
+ validations: 0,
22
+ valid_rows: 0,
23
+ invalid_rows: 0,
24
+ constraint_violations: 0,
25
+ validation_time_ms: 0,
26
+ total_validation_time_ms: 0,
27
+ avg_validation_time_ms: 0
28
+ }
29
+ # Validation calls get_constraints_for_table while holding the
30
+ # validator lock; use a re-entrant lock to avoid self-deadlock.
31
+ @lock = Monitor.new
32
+ @constraint_cache = {}
33
+ @deferred_constraints = []
34
+ end
35
+
36
+ def add_constraint(constraint)
37
+ @lock.synchronize do
38
+ type = constraint.type
39
+ @constraints[type] ||= []
40
+ @constraints[type] << constraint
41
+ @constraint_cache[constraint.name] = constraint
42
+ @constraint_cache["#{constraint.table_name}:#{constraint.name}"] = constraint
43
+ end
44
+ end
45
+
46
+ def remove_constraint(name)
47
+ @lock.synchronize do
48
+ @constraints.each do |type, list|
49
+ list.delete_if { |c| c.name == name }
50
+ end
51
+ @constraint_cache.delete(name)
52
+ @constraint_cache.each do |key, c|
53
+ if c.name == name
54
+ @constraint_cache.delete(key)
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def get_constraint(name)
61
+ @constraint_cache[name]
62
+ end
63
+
64
+ def get_constraints_for_table(table_name)
65
+ @lock.synchronize do
66
+ result = []
67
+ @constraints.each do |_, list|
68
+ list.each do |c|
69
+ result << c if c.table_name == table_name
70
+ end
71
+ end
72
+ result
73
+ end
74
+ end
75
+
76
+ def validate_row(row, table_name, options = {})
77
+ @lock.synchronize do
78
+ start_time = Time.now
79
+ @stats[:validations] += 1
80
+
81
+ result = {
82
+ valid: true,
83
+ errors: [],
84
+ warnings: [],
85
+ constraints_checked: [],
86
+ constraints_passed: [],
87
+ constraints_failed: []
88
+ }
89
+
90
+ constraints = get_constraints_for_table(table_name)
91
+
92
+ constraints.each do |constraint|
93
+ next unless constraint.enabled?
94
+ next if options[:skip] && options[:skip].include?(constraint.type)
95
+
96
+ @stats[:constraint_violations] += 1
97
+ result[:constraints_checked] << constraint.name
98
+
99
+ if constraint.validate(row)
100
+ result[:constraints_passed] << constraint.name
101
+ else
102
+ result[:valid] = false
103
+ result[:constraints_failed] << constraint.name
104
+ result[:errors] << {
105
+ constraint: constraint.name,
106
+ type: constraint.type,
107
+ message: constraint.validation_errors.last
108
+ }
109
+ end
110
+ end
111
+
112
+ # Handle deferred constraints
113
+ if options[:defer] != false
114
+ @deferred_constraints.each do |dc|
115
+ next unless dc[:table] == table_name
116
+
117
+ if options[:defer] == false
118
+ if dc[:constraint].validate(row)
119
+ result[:constraints_passed] << dc[:constraint].name
120
+ else
121
+ result[:valid] = false
122
+ result[:constraints_failed] << dc[:constraint].name
123
+ result[:errors] << {
124
+ constraint: dc[:constraint].name,
125
+ type: dc[:constraint].type,
126
+ message: "Deferred constraint violation"
127
+ }
128
+ end
129
+ elsif dc[:row_provider].nil?
130
+ dc[:captured_rows] << row
131
+ end
132
+ end
133
+ end
134
+
135
+ elapsed_ms = (Time.now - start_time) * 1000
136
+ @stats[:total_validation_time_ms] += elapsed_ms
137
+ @stats[:avg_validation_time_ms] = @stats[:total_validation_time_ms] / @stats[:validations]
138
+
139
+ if result[:valid]
140
+ @stats[:valid_rows] += 1
141
+ else
142
+ @stats[:invalid_rows] += 1
143
+ end
144
+
145
+ result
146
+ end
147
+ end
148
+
149
+ def validate_rows(rows, table_name, options = {})
150
+ @lock.synchronize do
151
+ start_time = Time.now
152
+ @stats[:validations] += 1
153
+
154
+ results = {
155
+ valid: [],
156
+ invalid: [],
157
+ errors: [],
158
+ summary: {
159
+ total: rows.size,
160
+ valid: 0,
161
+ invalid: 0,
162
+ constraint_violations: 0
163
+ }
164
+ }
165
+
166
+ constraints = get_constraints_for_table(table_name)
167
+
168
+ # Group constraints by type for batch validation
169
+ constraints_by_type = {}
170
+ constraints.each do |c|
171
+ next unless c.enabled?
172
+ next if options[:skip] && options[:skip].include?(c.type)
173
+
174
+ constraints_by_type[c.type] ||= []
175
+ constraints_by_type[c.type] << c
176
+ end
177
+
178
+ # Validate each row
179
+ rows.each do |row|
180
+ row_valid = true
181
+ row_errors = []
182
+
183
+ constraints_by_type.each do |type, list|
184
+ list.each do |constraint|
185
+ @stats[:constraint_violations] += 1
186
+
187
+ if constraint.validate(row)
188
+ # Passed
189
+ else
190
+ row_valid = false
191
+ row_errors << {
192
+ constraint: constraint.name,
193
+ type: type,
194
+ message: constraint.validation_errors.last
195
+ }
196
+ end
197
+ end
198
+ end
199
+
200
+ if row_valid
201
+ results[:valid] << row
202
+ results[:summary][:valid] += 1
203
+ else
204
+ results[:invalid] << row
205
+ results[:summary][:invalid] += 1
206
+ results[:errors] << { row: row, errors: row_errors }
207
+ results[:summary][:constraint_violations] += row_errors.size
208
+ end
209
+ end
210
+
211
+ elapsed_ms = (Time.now - start_time) * 1000
212
+ @stats[:total_validation_time_ms] += elapsed_ms
213
+ @stats[:avg_validation_time_ms] = @stats[:total_validation_time_ms] / @stats[:validations]
214
+
215
+ @stats[:valid_rows] += results[:summary][:valid]
216
+ @stats[:invalid_rows] += results[:summary][:invalid]
217
+
218
+ results
219
+ end
220
+ end
221
+
222
+ def validate_batch(table_name, rows, options = {})
223
+ validate_rows(rows, table_name, options)
224
+ end
225
+
226
+ def defer_constraint(constraint, table_name, rows: nil, row_provider: nil)
227
+ @lock.synchronize do
228
+ @deferred_constraints << {
229
+ constraint: constraint,
230
+ table: table_name,
231
+ rows: rows,
232
+ row_provider: row_provider,
233
+ captured_rows: [],
234
+ deferred_at: Time.now
235
+ }
236
+ end
237
+ end
238
+
239
+ def validate_deferred(table_name = nil, rows: nil)
240
+ @lock.synchronize do
241
+ results = {
242
+ valid: true,
243
+ errors: [],
244
+ constraints_checked: []
245
+ }
246
+
247
+ deferred = if table_name
248
+ @deferred_constraints.select { |dc| dc[:table] == table_name }
249
+ else
250
+ @deferred_constraints
251
+ end
252
+
253
+ deferred.each do |dc|
254
+ candidate_rows = rows || dc[:rows] || (dc[:captured_rows] unless dc[:captured_rows].empty?)
255
+ candidate_rows = dc[:row_provider].call if candidate_rows.nil? && dc[:row_provider]
256
+ candidate_rows = Array(candidate_rows) if candidate_rows
257
+
258
+ if candidate_rows.nil?
259
+ results[:valid] = false
260
+ results[:errors] << {
261
+ constraint: dc[:constraint].name,
262
+ error: "Deferred constraint validation requires rows or a row provider"
263
+ }
264
+ next
265
+ end
266
+
267
+ invalid_rows = candidate_rows.reject { |row| dc[:constraint].validate(row) }
268
+ results[:constraints_checked] << dc[:constraint].name
269
+ unless invalid_rows.empty?
270
+ results[:valid] = false
271
+ results[:errors] << {
272
+ constraint: dc[:constraint].name,
273
+ error: "Deferred constraint violation",
274
+ invalid_rows: invalid_rows
275
+ }
276
+ end
277
+ end
278
+
279
+ if results[:valid]
280
+ if table_name
281
+ @deferred_constraints.reject! { |dc| deferred.include?(dc) }
282
+ else
283
+ @deferred_constraints.clear
284
+ end
285
+ end
286
+ results
287
+ end
288
+ end
289
+
290
+ def check_constraints(table_name)
291
+ @lock.synchronize do
292
+ constraints = get_constraints_for_table(table_name)
293
+ results = {
294
+ total: constraints.size,
295
+ enabled: constraints.count(&:enabled?),
296
+ disabled: constraints.count { |c| !c.enabled? },
297
+ constraints: constraints.map(&:to_hash)
298
+ }
299
+ results
300
+ end
301
+ end
302
+
303
+ def enable_constraint(name)
304
+ @lock.synchronize do
305
+ constraint = get_constraint(name)
306
+ constraint&.enable
307
+ end
308
+ end
309
+
310
+ def disable_constraint(name)
311
+ @lock.synchronize do
312
+ constraint = get_constraint(name)
313
+ constraint&.disable
314
+ end
315
+ end
316
+
317
+ def stats
318
+ @lock.synchronize do
319
+ @stats.merge({
320
+ total_constraints: @constraints.values.flatten.size,
321
+ primary_keys: @constraints[:primary_key].size,
322
+ foreign_keys: @constraints[:foreign_key].size,
323
+ unique_constraints: @constraints[:unique].size,
324
+ not_null_constraints: @constraints[:not_null].size,
325
+ check_constraints: @constraints[:check].size,
326
+ deferred_constraints: @deferred_constraints.size,
327
+ cache_size: @constraint_cache.size
328
+ })
329
+ end
330
+ end
331
+
332
+ def clear
333
+ @lock.synchronize do
334
+ @constraints.each { |_, list| list.clear }
335
+ @constraint_cache.clear
336
+ @deferred_constraints.clear
337
+ @stats = {
338
+ validations: 0,
339
+ valid_rows: 0,
340
+ invalid_rows: 0,
341
+ constraint_violations: 0,
342
+ validation_time_ms: 0,
343
+ total_validation_time_ms: 0,
344
+ avg_validation_time_ms: 0
345
+ }
346
+ end
347
+ end
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when authentication fails
5
+ class AuthenticationError < Error
6
+ def initialize(message = "Authentication failed", details: nil)
7
+ super(message, code: ErrorCodes::PERMISSION_DENIED, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when authorization fails (permission denied)
5
+ class AuthorizationError < Error
6
+ attr_reader :user, :action, :resource
7
+
8
+ def initialize(message = "Permission denied", user: nil, action: nil, resource: nil, details: nil)
9
+ super(message, code: ErrorCodes::PERMISSION_DENIED, details: details)
10
+ @user = user
11
+ @action = action
12
+ @resource = resource
13
+ end
14
+
15
+ def to_s
16
+ msg = super
17
+ msg = "#{msg} for user #{@user}" if @user
18
+ msg = "#{msg} to #{@action}" if @action
19
+ msg = "#{msg} on #{@resource}" if @resource
20
+ msg
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised for client-side lifecycle and protocol usage errors.
5
+ class ClientError < Error
6
+ def initialize(message = "Client error", details: nil)
7
+ super(message, code: ErrorCodes::ERROR, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when there is an error in database configuration
5
+ class ConfigurationError < Error
6
+ def initialize(message = "Configuration error", details: nil)
7
+ super(message, code: ErrorCodes::ERROR, details: details)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ # Raised when there is an error connecting to the database
5
+ class ConnectionError < Error
6
+ def initialize(message = "Connection error", details: nil)
7
+ super(message, code: ErrorCodes::ERROR, details: details)
8
+ end
9
+ end
10
+ end