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,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "fileutils"
5
+ require "openssl"
6
+
7
+ module RubyDB
8
+ module Security
9
+ # Credentials - Manages credential storage
10
+ class Credentials
11
+ attr_reader :stats
12
+
13
+ def initialize(config = {})
14
+ @config = config
15
+ @storage_path = config[:storage_path] || "credentials.json"
16
+ @encryption_key = config[:encryption_key]
17
+ @credentials = {}
18
+ @loaded = false
19
+ @stats = {
20
+ loaded: 0,
21
+ saved: 0,
22
+ encrypted: 0,
23
+ decrypted: 0,
24
+ errors: 0
25
+ }
26
+ @lock = Mutex.new
27
+
28
+ load_credentials if File.exist?(@storage_path)
29
+ end
30
+
31
+ def add_credential(service, username, password, metadata = {})
32
+ @lock.synchronize do
33
+ @credentials[service] ||= {}
34
+ @credentials[service][username] = {
35
+ password: password,
36
+ metadata: metadata,
37
+ created_at: Time.now.iso8601,
38
+ updated_at: Time.now.iso8601
39
+ }
40
+ save_credentials
41
+ end
42
+ end
43
+
44
+ def get_credential(service, username)
45
+ @lock.synchronize do
46
+ load_credentials unless @loaded
47
+ return nil unless @credentials[service]
48
+ @credentials[service][username]
49
+ end
50
+ end
51
+
52
+ def remove_credential(service, username)
53
+ @lock.synchronize do
54
+ return false unless @credentials[service]
55
+ result = @credentials[service].delete(username)
56
+ save_credentials if result
57
+ result
58
+ end
59
+ end
60
+
61
+ def list_services
62
+ @lock.synchronize do
63
+ load_credentials unless @loaded
64
+ @credentials.keys
65
+ end
66
+ end
67
+
68
+ def list_usernames(service)
69
+ @lock.synchronize do
70
+ load_credentials unless @loaded
71
+ return [] unless @credentials[service]
72
+ @credentials[service].keys
73
+ end
74
+ end
75
+
76
+ def validate_credential(service, username, password)
77
+ @lock.synchronize do
78
+ cred = get_credential(service, username)
79
+ return false unless cred
80
+ cred[:password] == password
81
+ end
82
+ end
83
+
84
+ def update_credential(service, username, new_password, metadata = nil)
85
+ @lock.synchronize do
86
+ cred = get_credential(service, username)
87
+ return false unless cred
88
+ cred[:password] = new_password
89
+ cred[:updated_at] = Time.now.iso8601
90
+ cred[:metadata] = metadata if metadata
91
+ save_credentials
92
+ true
93
+ end
94
+ end
95
+
96
+ def encrypt_file
97
+ @lock.synchronize do
98
+ return unless @encryption_key
99
+
100
+ @stats[:encrypted] += 1
101
+ encrypted_path = "#{@storage_path}.encrypted"
102
+
103
+ # Read file
104
+ data = File.read(@storage_path)
105
+
106
+ # Encrypt
107
+ cipher = OpenSSL::Cipher.new("aes-256-cbc")
108
+ cipher.encrypt
109
+ cipher.key = @encryption_key
110
+ cipher.iv = cipher.random_iv
111
+
112
+ encrypted = cipher.update(data) + cipher.final
113
+ iv = cipher.iv
114
+
115
+ # Write encrypted
116
+ File.write(encrypted_path, iv + encrypted)
117
+ File.delete(@storage_path)
118
+
119
+ @storage_path = encrypted_path
120
+ end
121
+ end
122
+
123
+ def decrypt_file
124
+ @lock.synchronize do
125
+ return unless @encryption_key && File.exist?(@storage_path)
126
+
127
+ @stats[:decrypted] += 1
128
+ decrypted_path = @storage_path.gsub(/\.encrypted$/, ".json")
129
+
130
+ # Read encrypted
131
+ data = File.read(@storage_path)
132
+
133
+ # Decrypt
134
+ iv = data[0, 16]
135
+ encrypted = data[16..-1]
136
+
137
+ cipher = OpenSSL::Cipher.new("aes-256-cbc")
138
+ cipher.decrypt
139
+ cipher.key = @encryption_key
140
+ cipher.iv = iv
141
+
142
+ decrypted = cipher.update(encrypted) + cipher.final
143
+
144
+ # Write decrypted
145
+ File.write(decrypted_path, decrypted)
146
+ File.delete(@storage_path)
147
+
148
+ @storage_path = decrypted_path
149
+ load_credentials
150
+ end
151
+ end
152
+
153
+ def stats
154
+ @lock.synchronize do
155
+ @stats.merge({
156
+ credentials_count: @credentials.size,
157
+ services_count: @credentials.keys.size,
158
+ loaded: @loaded,
159
+ storage_path: @storage_path,
160
+ encrypted: @storage_path.end_with?(".encrypted")
161
+ })
162
+ end
163
+ end
164
+
165
+ private
166
+
167
+ def load_credentials
168
+ @lock.synchronize do
169
+ begin
170
+ if File.exist?(@storage_path)
171
+ data = File.read(@storage_path)
172
+ @credentials = JSON.parse(data, symbolize_names: true)
173
+ @loaded = true
174
+ @stats[:loaded] += 1
175
+ end
176
+ rescue => e
177
+ @stats[:errors] += 1
178
+ @credentials = {}
179
+ end
180
+ end
181
+ end
182
+
183
+ def save_credentials
184
+ @lock.synchronize do
185
+ begin
186
+ data = JSON.generate(@credentials)
187
+ File.write(@storage_path, data)
188
+ @stats[:saved] += 1
189
+ rescue => e
190
+ @stats[:errors] += 1
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "securerandom"
5
+ require "base64"
6
+ require "monitor"
7
+
8
+ module RubyDB
9
+ module Security
10
+ # Password - Handles password hashing and validation
11
+ class Password
12
+ # Password algorithms
13
+ ALGORITHM_SHA256 = :sha256
14
+ ALGORITHM_SHA512 = :sha512
15
+ ALGORITHM_BCRYPT = :bcrypt
16
+ ALGORITHM_PBKDF2 = :pbkdf2
17
+ ALGORITHM_ARGON2 = :argon2
18
+
19
+ def initialize(config = {})
20
+ @algorithm = config[:algorithm] || ALGORITHM_SHA256
21
+ @iterations = config[:iterations] || 10000
22
+ @salt_length = config[:salt_length] || 16
23
+ @key_length = config[:key_length] || 32
24
+ @bcrypt_cost = config[:bcrypt_cost] || 10
25
+ @stats = {
26
+ hashes: 0,
27
+ verifications: 0,
28
+ verifications_successful: 0,
29
+ verifications_failed: 0,
30
+ salts_generated: 0
31
+ }
32
+ @lock = Monitor.new
33
+ end
34
+
35
+ def hash(password, salt = nil)
36
+ @lock.synchronize do
37
+ @stats[:hashes] += 1
38
+ salt ||= generate_salt
39
+
40
+ hash_data = case @algorithm
41
+ when ALGORITHM_SHA256
42
+ hash_sha256(password, salt)
43
+ when ALGORITHM_SHA512
44
+ hash_sha512(password, salt)
45
+ when ALGORITHM_BCRYPT
46
+ hash_bcrypt(password)
47
+ when ALGORITHM_PBKDF2
48
+ hash_pbkdf2(password, salt)
49
+ when ALGORITHM_ARGON2
50
+ hash_argon2(password, salt)
51
+ else
52
+ raise ArgumentError, "Unsupported password algorithm: #{@algorithm}"
53
+ end
54
+
55
+ {
56
+ algorithm: @algorithm,
57
+ salt: salt,
58
+ hash: hash_data,
59
+ iterations: @iterations,
60
+ key_length: @key_length
61
+ }
62
+ end
63
+ end
64
+
65
+ def verify(password, stored_hash)
66
+ @lock.synchronize do
67
+ @stats[:verifications] += 1
68
+
69
+ algorithm = stored_hash[:algorithm] || @algorithm
70
+ salt = stored_hash[:salt]
71
+ hash_data = stored_hash[:hash]
72
+
73
+ result = case algorithm
74
+ when ALGORITHM_SHA256
75
+ verify_sha256(password, salt, hash_data)
76
+ when ALGORITHM_SHA512
77
+ verify_sha512(password, salt, hash_data)
78
+ when ALGORITHM_BCRYPT
79
+ verify_bcrypt(password, hash_data)
80
+ when ALGORITHM_PBKDF2
81
+ verify_pbkdf2(password, salt, hash_data, stored_hash[:iterations] || @iterations)
82
+ when ALGORITHM_ARGON2
83
+ verify_argon2(password, salt, hash_data)
84
+ else
85
+ false
86
+ end
87
+
88
+ if result
89
+ @stats[:verifications_successful] += 1
90
+ else
91
+ @stats[:verifications_failed] += 1
92
+ end
93
+
94
+ result
95
+ end
96
+ end
97
+
98
+ def generate_salt
99
+ @lock.synchronize do
100
+ @stats[:salts_generated] += 1
101
+ SecureRandom.hex(@salt_length)
102
+ end
103
+ end
104
+
105
+ def meets_policy?(password)
106
+ # Check password policy
107
+ return false if password.length < 8
108
+ return false unless password.match?(/[a-z]/)
109
+ return false unless password.match?(/[A-Z]/)
110
+ return false unless password.match?(/\d/)
111
+ return false unless password.match?(/[^a-zA-Z0-9]/)
112
+ true
113
+ end
114
+
115
+ def generate_random_password(length = 16)
116
+ charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()"
117
+ (0...length).map { charset[SecureRandom.rand(charset.length)] }.join
118
+ end
119
+
120
+ def stats
121
+ @lock.synchronize do
122
+ @stats.merge({
123
+ algorithm: @algorithm,
124
+ iterations: @iterations,
125
+ salt_length: @salt_length,
126
+ key_length: @key_length
127
+ })
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ def hash_sha256(password, salt)
134
+ Digest::SHA256.hexdigest(salt + password)
135
+ end
136
+
137
+ def verify_sha256(password, salt, stored_hash)
138
+ hash_sha256(password, salt) == stored_hash
139
+ end
140
+
141
+ def hash_sha512(password, salt)
142
+ Digest::SHA512.hexdigest(salt + password)
143
+ end
144
+
145
+ def verify_sha512(password, salt, stored_hash)
146
+ hash_sha512(password, salt) == stored_hash
147
+ end
148
+
149
+ def hash_bcrypt(password)
150
+ begin
151
+ require "bcrypt"
152
+ BCrypt::Password.create(password, cost: @bcrypt_cost)
153
+ rescue LoadError
154
+ raise ArgumentError, "bcrypt support requires the bcrypt gem"
155
+ end
156
+ end
157
+
158
+ def verify_bcrypt(password, stored_hash)
159
+ begin
160
+ require "bcrypt"
161
+ BCrypt::Password.new(stored_hash) == password
162
+ rescue LoadError
163
+ false
164
+ end
165
+ end
166
+
167
+ def hash_pbkdf2(password, salt)
168
+ begin
169
+ require "openssl"
170
+ OpenSSL::PKCS5.pbkdf2_hmac(
171
+ password,
172
+ salt,
173
+ @iterations,
174
+ @key_length,
175
+ "sha256"
176
+ ).unpack1("H*")
177
+ rescue LoadError
178
+ hash_sha256(password, salt)
179
+ end
180
+ end
181
+
182
+ def verify_pbkdf2(password, salt, stored_hash, iterations)
183
+ require "openssl"
184
+ computed = OpenSSL::PKCS5.pbkdf2_hmac(
185
+ password,
186
+ salt,
187
+ iterations,
188
+ @key_length,
189
+ "sha256"
190
+ ).unpack1("H*")
191
+ computed == stored_hash
192
+ rescue LoadError
193
+ false
194
+ end
195
+
196
+ def hash_argon2(password, salt)
197
+ begin
198
+ require "argon2"
199
+ Argon2::Password.create(password)
200
+ rescue LoadError
201
+ raise ArgumentError, "argon2 support requires the argon2 gem"
202
+ end
203
+ end
204
+
205
+ def verify_argon2(password, salt, stored_hash)
206
+ begin
207
+ require "argon2"
208
+ Argon2::Password.verify_password(password, stored_hash)
209
+ rescue LoadError
210
+ false
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Security
5
+ # Permissions - Permission definitions and management
6
+ class Permissions
7
+ # Standard permissions
8
+ SELECT = "SELECT"
9
+ INSERT = "INSERT"
10
+ UPDATE = "UPDATE"
11
+ DELETE = "DELETE"
12
+ CREATE = "CREATE"
13
+ DROP = "DROP"
14
+ ALTER = "ALTER"
15
+ INDEX = "INDEX"
16
+ GRANT = "GRANT"
17
+ REVOKE = "REVOKE"
18
+ EXECUTE = "EXECUTE"
19
+ CONNECT = "CONNECT"
20
+ USAGE = "USAGE"
21
+ ALL = "ALL"
22
+
23
+ # Permission groups
24
+ GROUPS = {
25
+ read: [SELECT],
26
+ write: [INSERT, UPDATE, DELETE],
27
+ ddl: [CREATE, DROP, ALTER],
28
+ admin: [GRANT, REVOKE],
29
+ all: [SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, EXECUTE]
30
+ }
31
+
32
+ def initialize
33
+ @permissions = {}
34
+ @lock = Mutex.new
35
+ end
36
+
37
+ def define_permission(name, description = nil)
38
+ @lock.synchronize do
39
+ @permissions[name] = {
40
+ description: description || "Permission to #{name}",
41
+ created_at: Time.now
42
+ }
43
+ end
44
+ end
45
+
46
+ def permission_exists?(name)
47
+ @permissions.key?(name)
48
+ end
49
+
50
+ def list_permissions
51
+ @permissions.keys
52
+ end
53
+
54
+ def describe_permission(name)
55
+ @permissions[name]
56
+ end
57
+
58
+ def group_permissions(group)
59
+ GROUPS[group] || []
60
+ end
61
+
62
+ def permission_names
63
+ @permissions.keys
64
+ end
65
+
66
+ def to_hash
67
+ {
68
+ permissions: @permissions,
69
+ groups: GROUPS
70
+ }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module RubyDB
6
+ module Security
7
+ # Role - Role-based access control
8
+ class Role
9
+ attr_reader :name, :created_at, :metadata, :members
10
+ attr_accessor :description, :parent_role
11
+
12
+ def initialize(name, options = {})
13
+ @name = name
14
+ @description = options[:description]
15
+ @parent_role = options[:parent_role]
16
+ @members = Set.new
17
+ @permissions = []
18
+ @inherited_permissions = nil
19
+ @created_at = Time.now
20
+ @metadata = options[:metadata] || {}
21
+ end
22
+
23
+ def add_member(username)
24
+ @members.add(username)
25
+ invalidate_cache
26
+ end
27
+
28
+ def remove_member(username)
29
+ @members.delete(username)
30
+ invalidate_cache
31
+ end
32
+
33
+ def member?(username)
34
+ @members.include?(username)
35
+ end
36
+
37
+ def members
38
+ @members.to_a
39
+ end
40
+
41
+ def add_permission(permission)
42
+ @permissions << permission unless @permissions.include?(permission)
43
+ invalidate_cache
44
+ end
45
+
46
+ def remove_permission(permission)
47
+ @permissions.delete(permission)
48
+ invalidate_cache
49
+ end
50
+
51
+ def permissions
52
+ @permissions.dup
53
+ end
54
+
55
+ def all_permissions(role_map = {})
56
+ if @inherited_permissions.nil?
57
+ @inherited_permissions = compute_all_permissions(role_map)
58
+ end
59
+ @inherited_permissions
60
+ end
61
+
62
+ def inherit_from(parent_role)
63
+ @parent_role = parent_role
64
+ invalidate_cache
65
+ end
66
+
67
+ def to_hash
68
+ {
69
+ name: @name,
70
+ description: @description,
71
+ parent_role: @parent_role,
72
+ members: @members.to_a,
73
+ permissions: @permissions,
74
+ created_at: @created_at.iso8601,
75
+ metadata: @metadata
76
+ }
77
+ end
78
+
79
+ def inspect
80
+ "#<Role name=#{@name} members=#{@members.size} permissions=#{@permissions.size}>"
81
+ end
82
+
83
+ private
84
+
85
+ def invalidate_cache
86
+ @inherited_permissions = nil
87
+ end
88
+
89
+ def compute_all_permissions(role_map)
90
+ perms = @permissions.dup
91
+
92
+ if @parent_role && role_map[@parent_role]
93
+ parent = role_map[@parent_role]
94
+ perms.concat(parent.all_permissions(role_map))
95
+ end
96
+
97
+ perms.uniq
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module RubyDB
6
+ module Security
7
+ # User - User account
8
+ class User
9
+ attr_reader :username, :created_at, :last_login, :attributes
10
+ attr_accessor :password_hash, :salt, :email, :full_name, :superuser
11
+ attr_accessor :active, :locked, :expires_at, :metadata
12
+
13
+ def initialize(username, options = {})
14
+ @username = username
15
+ @password_hash = options[:password_hash]
16
+ @salt = options[:salt]
17
+ @email = options[:email]
18
+ @full_name = options[:full_name]
19
+ @superuser = options[:superuser] || false
20
+ @active = options[:active] != false
21
+ @locked = options[:locked] || false
22
+ @created_at = Time.now
23
+ @last_login = nil
24
+ @expires_at = options[:expires_at]
25
+ @metadata = options[:metadata] || {}
26
+ @attributes = options[:attributes] || {}
27
+ end
28
+
29
+ def authenticate(password, password_hasher)
30
+ return false unless @active && !@locked
31
+ return false if expired?
32
+ password_hasher.verify(password, @password_hash)
33
+ end
34
+
35
+ def login
36
+ @last_login = Time.now
37
+ @locked = false
38
+ end
39
+
40
+ def lock
41
+ @locked = true
42
+ end
43
+
44
+ def unlock
45
+ @locked = false
46
+ end
47
+
48
+ def activate
49
+ @active = true
50
+ end
51
+
52
+ def deactivate
53
+ @active = false
54
+ end
55
+
56
+ def expired?
57
+ return false unless @expires_at
58
+ Time.now > @expires_at
59
+ end
60
+
61
+ def to_hash
62
+ {
63
+ username: @username,
64
+ email: @email,
65
+ full_name: @full_name,
66
+ superuser: @superuser,
67
+ active: @active,
68
+ locked: @locked,
69
+ created_at: @created_at.iso8601,
70
+ last_login: @last_login&.iso8601,
71
+ expires_at: @expires_at&.iso8601,
72
+ metadata: @metadata,
73
+ attributes: @attributes
74
+ }
75
+ end
76
+
77
+ def to_json
78
+ JSON.generate(to_hash)
79
+ end
80
+
81
+ def inspect
82
+ "#<User username=#{@username} active=#{@active} superuser=#{@superuser}>"
83
+ end
84
+ end
85
+ end
86
+ end