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,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Server
5
+ # WorkerPool - Manages worker threads
6
+ class WorkerPool
7
+ attr_reader :stats
8
+
9
+ def initialize(config, engine, transaction_manager)
10
+ @config = config
11
+ @engine = engine
12
+ @transaction_manager = transaction_manager
13
+ @min_workers = config[:min_workers] || 2
14
+ @max_workers = config[:max_workers] || 20
15
+ @workers = []
16
+ @worker_counter = 0
17
+ @stats = {
18
+ total_workers: 0,
19
+ active_workers: 0,
20
+ idle_workers: 0,
21
+ workers_created: 0,
22
+ workers_destroyed: 0,
23
+ total_requests: 0,
24
+ queue_size: 0
25
+ }
26
+ @lock = Mutex.new
27
+ @running = false
28
+ @queue = Queue.new
29
+ @cleanup_thread = nil
30
+ @scaling_thread = nil
31
+ end
32
+
33
+ def start
34
+ @lock.synchronize do
35
+ return if @running
36
+
37
+ @running = true
38
+
39
+ # Create initial workers
40
+ @min_workers.times { create_worker }
41
+
42
+ # Start scaling thread
43
+ @scaling_thread = Thread.new { scaling_loop }
44
+
45
+ # Start cleanup thread
46
+ @cleanup_thread = Thread.new { cleanup_loop }
47
+
48
+ true
49
+ end
50
+ end
51
+
52
+ def stop
53
+ @lock.synchronize do
54
+ return unless @running
55
+
56
+ @running = false
57
+
58
+ # Stop scaling thread
59
+ if @scaling_thread
60
+ @scaling_thread.kill
61
+ @scaling_thread = nil
62
+ end
63
+
64
+ # Stop cleanup thread
65
+ if @cleanup_thread
66
+ @cleanup_thread.kill
67
+ @cleanup_thread = nil
68
+ end
69
+
70
+ # Stop all workers
71
+ @workers.each(&:stop)
72
+ @workers.clear
73
+
74
+ true
75
+ end
76
+ end
77
+
78
+ def submit(request)
79
+ @lock.synchronize do
80
+ return false unless @running
81
+
82
+ @queue << request
83
+ @stats[:total_requests] += 1
84
+ @stats[:queue_size] = @queue.size
85
+
86
+ # Find idle worker
87
+ worker = find_idle_worker
88
+
89
+ if worker
90
+ worker.submit(request)
91
+ else
92
+ # Queue the request
93
+ @queue << request
94
+ end
95
+
96
+ true
97
+ end
98
+ end
99
+
100
+ def stats
101
+ @lock.synchronize do
102
+ @stats.merge({
103
+ workers: @workers.size,
104
+ running: @running,
105
+ min_workers: @min_workers,
106
+ max_workers: @max_workers,
107
+ queue_size: @queue.size
108
+ })
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def create_worker
115
+ @worker_counter += 1
116
+ worker = Worker.new(@worker_counter, @config, @engine, @transaction_manager)
117
+ worker.start
118
+ @workers << worker
119
+ @stats[:workers_created] += 1
120
+ @stats[:total_workers] = @workers.size
121
+ worker
122
+ end
123
+
124
+ def destroy_worker(worker)
125
+ worker.stop
126
+ @workers.delete(worker)
127
+ @stats[:workers_destroyed] += 1
128
+ @stats[:total_workers] = @workers.size
129
+ end
130
+
131
+ def find_idle_worker
132
+ @workers.find { |w| !w.busy? }
133
+ end
134
+
135
+ def scaling_loop
136
+ while @running
137
+ sleep(5)
138
+
139
+ @lock.synchronize do
140
+ # Scale up if queue is growing
141
+ if @queue.size > @workers.size * 2 && @workers.size < @max_workers
142
+ create_worker
143
+ end
144
+
145
+ # Scale down if queue is empty and we have extra workers
146
+ if @queue.empty? && @workers.size > @min_workers
147
+ idle_workers = @workers.select { |w| !w.busy? }
148
+ if idle_workers.size > 1
149
+ destroy_worker(idle_workers.first)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ def cleanup_loop
157
+ while @running
158
+ sleep(60)
159
+
160
+ @lock.synchronize do
161
+ # Remove dead workers
162
+ @workers.reject! { |w| !w.running? }
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # ALTER TABLE statement AST node (base)
7
+ class AlterTable < Node
8
+ attr_reader :table_name
9
+
10
+ def initialize(table_name, location: nil)
11
+ super(location: location)
12
+ @table_name = table_name
13
+ end
14
+
15
+ def to_sql
16
+ "ALTER TABLE #{@table_name}"
17
+ end
18
+
19
+ def inspect
20
+ "AlterTable(table: #{@table_name})"
21
+ end
22
+ end
23
+
24
+ # ALTER TABLE ADD COLUMN
25
+ class AlterTableAddColumn < AlterTable
26
+ attr_reader :column_name, :column_type, :options
27
+
28
+ def initialize(table_name, column_name, column_type, options = {}, location: nil)
29
+ super(table_name, location: location)
30
+ @column_name = column_name
31
+ @column_type = column_type
32
+ @options = options
33
+ end
34
+
35
+ def accept(visitor)
36
+ visitor.visit_alter_table_add_column(self)
37
+ end
38
+
39
+ def clone
40
+ AlterTableAddColumn.new(
41
+ @table_name,
42
+ @column_name,
43
+ @column_type.is_a?(Hash) ? @column_type.dup : @column_type,
44
+ @options.dup,
45
+ location: @location
46
+ )
47
+ end
48
+
49
+ def to_sql
50
+ parts = [super]
51
+ parts << "ADD"
52
+ parts << @column_name
53
+ parts << type_sql
54
+ parts << "NOT NULL" if @options[:null] == false
55
+ parts << "NULL" if @options[:null] == true
56
+ parts << "DEFAULT #{@options[:default].to_sql}" if @options[:default]
57
+ if @options[:references]
58
+ ref = @options[:references]
59
+ parts << "REFERENCES #{ref[:table]}(#{ref[:column]})"
60
+ end
61
+ parts.join(" ")
62
+ end
63
+
64
+ def inspect
65
+ "AlterTableAddColumn(table: #{@table_name}, column: #{@column_name}, type: #{type_sql}, options: #{@options.inspect})"
66
+ end
67
+
68
+ def type_sql
69
+ case @column_type
70
+ when Symbol
71
+ @column_type.to_s.upcase
72
+ when Hash
73
+ if @column_type[:type] == :decimal
74
+ "DECIMAL(#{@column_type[:precision]}, #{@column_type[:scale]})"
75
+ elsif @column_type[:type] == :varchar
76
+ "VARCHAR(#{@column_type[:limit]})"
77
+ else
78
+ @column_type[:type].to_s.upcase
79
+ end
80
+ else
81
+ @column_type.to_s
82
+ end
83
+ end
84
+ end
85
+
86
+ # ALTER TABLE DROP COLUMN
87
+ class AlterTableDropColumn < AlterTable
88
+ attr_reader :column_name
89
+
90
+ def initialize(table_name, column_name, location: nil)
91
+ super(table_name, location: location)
92
+ @column_name = column_name
93
+ end
94
+
95
+ def accept(visitor)
96
+ visitor.visit_alter_table_drop_column(self)
97
+ end
98
+
99
+ def clone
100
+ AlterTableDropColumn.new(@table_name, @column_name, location: @location)
101
+ end
102
+
103
+ def to_sql
104
+ "#{super} DROP COLUMN #{@column_name}"
105
+ end
106
+
107
+ def inspect
108
+ "AlterTableDropColumn(table: #{@table_name}, column: #{@column_name})"
109
+ end
110
+ end
111
+
112
+ # ALTER TABLE ADD CONSTRAINT
113
+ class AlterTableAddConstraint < AlterTable
114
+ attr_reader :constraint
115
+
116
+ def initialize(table_name, constraint, location: nil)
117
+ super(table_name, location: location)
118
+ @constraint = constraint
119
+ end
120
+
121
+ def accept(visitor)
122
+ visitor.visit_alter_table_add_constraint(self)
123
+ end
124
+
125
+ def clone
126
+ AlterTableAddConstraint.new(
127
+ @table_name,
128
+ @constraint.clone,
129
+ location: @location
130
+ )
131
+ end
132
+
133
+ def to_sql
134
+ "#{super} ADD #{@constraint.to_sql}"
135
+ end
136
+
137
+ def inspect
138
+ "AlterTableAddConstraint(table: #{@table_name}, constraint: #{@constraint.inspect})"
139
+ end
140
+ end
141
+
142
+ # ALTER TABLE DROP CONSTRAINT
143
+ class AlterTableDropConstraint < AlterTable
144
+ attr_reader :constraint_name
145
+
146
+ def initialize(table_name, constraint_name, location: nil)
147
+ super(table_name, location: location)
148
+ @constraint_name = constraint_name
149
+ end
150
+
151
+ def accept(visitor)
152
+ visitor.visit_alter_table_drop_constraint(self)
153
+ end
154
+
155
+ def clone
156
+ AlterTableDropConstraint.new(@table_name, @constraint_name, location: @location)
157
+ end
158
+
159
+ def to_sql
160
+ "#{super} DROP CONSTRAINT #{@constraint_name}"
161
+ end
162
+
163
+ def inspect
164
+ "AlterTableDropConstraint(table: #{@table_name}, constraint: #{@constraint_name})"
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # BEGIN TRANSACTION statement AST node
7
+ class BeginTransaction < Node
8
+ attr_reader :isolation_level, :read_only
9
+
10
+ def initialize(isolation_level: nil, read_only: false, location: nil)
11
+ super(location: location)
12
+ @isolation_level = isolation_level
13
+ @read_only = read_only
14
+ end
15
+
16
+ def accept(visitor)
17
+ visitor.visit_begin_transaction(self)
18
+ end
19
+
20
+ def clone
21
+ BeginTransaction.new(
22
+ isolation_level: @isolation_level,
23
+ read_only: @read_only,
24
+ location: @location
25
+ )
26
+ end
27
+
28
+ def to_sql
29
+ parts = ["BEGIN"]
30
+ if @isolation_level
31
+ parts << "ISOLATION LEVEL"
32
+ parts << @isolation_level.to_s.upcase
33
+ end
34
+ parts << "READ ONLY" if @read_only
35
+ parts.join(" ")
36
+ end
37
+
38
+ def inspect
39
+ str = "BeginTransaction"
40
+ str << " (isolation_level: #{@isolation_level})" if @isolation_level
41
+ str << " (read_only: true)" if @read_only
42
+ str
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # COMMIT statement AST node
7
+ class Commit < Node
8
+ attr_reader :chain
9
+
10
+ def initialize(chain: false, location: nil)
11
+ super(location: location)
12
+ @chain = chain
13
+ end
14
+
15
+ def accept(visitor)
16
+ visitor.visit_commit(self)
17
+ end
18
+
19
+ def clone
20
+ Commit.new(chain: @chain, location: @location)
21
+ end
22
+
23
+ def to_sql
24
+ parts = ["COMMIT"]
25
+ parts << "AND CHAIN" if @chain
26
+ parts.join(" ")
27
+ end
28
+
29
+ def inspect
30
+ str = "Commit"
31
+ str << " (chain: true)" if @chain
32
+ str
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ class ConstraintDefinition < Node
7
+ attr_reader :name
8
+
9
+ def initialize(name, location: nil)
10
+ super(location: location)
11
+ @name = name
12
+ end
13
+
14
+ def accept(visitor)
15
+ visitor.public_send("visit_#{constraint_visit_name}", self)
16
+ end
17
+
18
+ def clone
19
+ self.class.new(@name, location: @location)
20
+ end
21
+
22
+ private
23
+
24
+ def constraint_visit_name
25
+ self.class.name.split("::").last.gsub(/([a-z\d])([A-Z])/, '\\1_\\2').downcase
26
+ end
27
+ end
28
+
29
+ class PrimaryKeyConstraint < ConstraintDefinition
30
+ attr_reader :columns
31
+ def initialize(name, columns, location: nil)
32
+ super(name, location: location)
33
+ @columns = Array(columns)
34
+ end
35
+ def clone = self.class.new(@name, @columns.dup, location: @location)
36
+ def to_sql = "CONSTRAINT #{@name} PRIMARY KEY (#{@columns.join(', ')})"
37
+ end
38
+
39
+ class ForeignKeyConstraint < ConstraintDefinition
40
+ attr_reader :columns, :reference_table, :reference_columns, :on_delete, :on_update
41
+ def initialize(name, columns, reference_table, reference_columns, on_delete: nil, on_update: nil, location: nil)
42
+ super(name, location: location)
43
+ @columns = Array(columns)
44
+ @reference_table = reference_table
45
+ @reference_columns = Array(reference_columns)
46
+ @on_delete = on_delete
47
+ @on_update = on_update
48
+ end
49
+ def clone = self.class.new(@name, @columns.dup, @reference_table, @reference_columns.dup, on_delete: @on_delete, on_update: @on_update, location: @location)
50
+ def to_sql
51
+ sql = "CONSTRAINT #{@name} FOREIGN KEY (#{@columns.join(', ')}) REFERENCES #{@reference_table} (#{@reference_columns.join(', ')})"
52
+ sql << " ON DELETE #{@on_delete.to_s.upcase.tr('_', ' ')}" if @on_delete
53
+ sql << " ON UPDATE #{@on_update.to_s.upcase.tr('_', ' ')}" if @on_update
54
+ sql
55
+ end
56
+ end
57
+
58
+ class UniqueConstraint < ConstraintDefinition
59
+ attr_reader :columns
60
+ def initialize(name, columns, location: nil)
61
+ super(name, location: location)
62
+ @columns = Array(columns)
63
+ end
64
+ def clone = self.class.new(@name, @columns.dup, location: @location)
65
+ def to_sql = "CONSTRAINT #{@name} UNIQUE (#{@columns.join(', ')})"
66
+ end
67
+
68
+ class CheckConstraint < ConstraintDefinition
69
+ attr_reader :condition
70
+ def initialize(name, condition, location: nil)
71
+ super(name, location: location)
72
+ @condition = condition
73
+ end
74
+ def accept(visitor)
75
+ super
76
+ @condition.accept(visitor) if @condition.respond_to?(:accept)
77
+ end
78
+ def clone = self.class.new(@name, @condition.clone, location: @location)
79
+ def to_sql = "CONSTRAINT #{@name} CHECK (#{@condition.to_sql})"
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # CREATE DATABASE statement AST node
7
+ class CreateDatabase < Node
8
+ attr_reader :name, :if_not_exists
9
+
10
+ def initialize(name, if_not_exists: false, location: nil)
11
+ super(location: location)
12
+ @name = name
13
+ @if_not_exists = if_not_exists
14
+ end
15
+
16
+ def accept(visitor)
17
+ visitor.visit_create_database(self)
18
+ end
19
+
20
+ def clone
21
+ CreateDatabase.new(@name, if_not_exists: @if_not_exists, location: @location)
22
+ end
23
+
24
+ def to_sql
25
+ parts = []
26
+ parts << "CREATE DATABASE"
27
+ parts << "IF NOT EXISTS" if @if_not_exists
28
+ parts << @name
29
+ parts.join(" ")
30
+ end
31
+
32
+ def inspect
33
+ str = "CreateDatabase(name: #{@name}"
34
+ str << ", if_not_exists: true" if @if_not_exists
35
+ str << ")"
36
+ str
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # CREATE INDEX statement AST node
7
+ class CreateIndex < Node
8
+ attr_reader :name, :table_name, :columns, :unique, :if_not_exists
9
+
10
+ def initialize(name, table_name, columns, unique: false, if_not_exists: false, location: nil)
11
+ super(location: location)
12
+ @name = name
13
+ @table_name = table_name
14
+ @columns = columns
15
+ @unique = unique
16
+ @if_not_exists = if_not_exists
17
+ end
18
+
19
+ def accept(visitor)
20
+ visitor.visit_create_index(self)
21
+ end
22
+
23
+ def clone
24
+ CreateIndex.new(
25
+ @name,
26
+ @table_name,
27
+ @columns.dup,
28
+ unique: @unique,
29
+ if_not_exists: @if_not_exists,
30
+ location: @location
31
+ )
32
+ end
33
+
34
+ def to_sql
35
+ parts = []
36
+ parts << "CREATE"
37
+ parts << "UNIQUE" if @unique
38
+ parts << "INDEX"
39
+ parts << "IF NOT EXISTS" if @if_not_exists
40
+ parts << @name
41
+ parts << "ON"
42
+ parts << @table_name
43
+ parts << "(#{@columns.join(", ")})"
44
+ parts.join(" ")
45
+ end
46
+
47
+ def inspect
48
+ str = "CreateIndex(name: #{@name}, table: #{@table_name}, columns: [#{@columns.join(", ")}]"
49
+ str << ", unique: true" if @unique
50
+ str << ", if_not_exists: true" if @if_not_exists
51
+ str << ")"
52
+ str
53
+ end
54
+
55
+ def column_count
56
+ @columns.size
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # CREATE SCHEMA statement AST node
7
+ class CreateSchema < Node
8
+ attr_reader :name, :if_not_exists, :authorization
9
+
10
+ def initialize(name, if_not_exists: false, authorization: nil, location: nil)
11
+ super(location: location)
12
+ @name = name
13
+ @if_not_exists = if_not_exists
14
+ @authorization = authorization
15
+ end
16
+
17
+ def accept(visitor)
18
+ visitor.visit_create_schema(self)
19
+ end
20
+
21
+ def clone
22
+ CreateSchema.new(
23
+ @name,
24
+ if_not_exists: @if_not_exists,
25
+ authorization: @authorization,
26
+ location: @location
27
+ )
28
+ end
29
+
30
+ def to_sql
31
+ parts = []
32
+ parts << "CREATE SCHEMA"
33
+ parts << "IF NOT EXISTS" if @if_not_exists
34
+ parts << @name
35
+ if @authorization
36
+ parts << "AUTHORIZATION"
37
+ parts << @authorization
38
+ end
39
+ parts.join(" ")
40
+ end
41
+
42
+ def inspect
43
+ str = "CreateSchema(name: #{@name}"
44
+ str << ", if_not_exists: true" if @if_not_exists
45
+ str << ", authorization: #{@authorization}" if @authorization
46
+ str << ")"
47
+ str
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end