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,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # Predicate - Represents conditions in WHERE clause
6
+ class Predicate
7
+ attr_reader :type
8
+
9
+ def initialize(type)
10
+ @type = type
11
+ end
12
+
13
+ def evaluate(row)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def to_s
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def negate
22
+ Predicate::Not.new(self)
23
+ end
24
+
25
+ # Comparison predicate: column = value
26
+ class Comparison < Predicate
27
+ attr_reader :left, :right, :operator
28
+
29
+ def initialize(left, right, operator)
30
+ super(:comparison)
31
+ @left = left
32
+ @right = right
33
+ @operator = operator
34
+ end
35
+
36
+ def evaluate(row)
37
+ left_val = @left.evaluate(row)
38
+ right_val = @right.evaluate(row)
39
+
40
+ return false if left_val.nil? || right_val.nil?
41
+
42
+ case @operator
43
+ when :eq then left_val == right_val
44
+ when :ne then left_val != right_val
45
+ when :lt then left_val < right_val
46
+ when :lte then left_val <= right_val
47
+ when :gt then left_val > right_val
48
+ when :gte then left_val >= right_val
49
+ when :like then like_match?(left_val.to_s, right_val.to_s)
50
+ else false
51
+ end
52
+ end
53
+
54
+ def to_s
55
+ op = case @operator
56
+ when :eq then "="
57
+ when :ne then "!="
58
+ when :lt then "<"
59
+ when :lte then "<="
60
+ when :gt then ">"
61
+ when :gte then ">="
62
+ when :like then "LIKE"
63
+ else @operator.to_s
64
+ end
65
+ "#{@left} #{op} #{@right}"
66
+ end
67
+
68
+ private
69
+
70
+ def like_match?(value, pattern)
71
+ regex_str = Regexp.escape(pattern)
72
+ .gsub('%', '.*')
73
+ .gsub('_', '.')
74
+ Regexp.new("^#{regex_str}$", Regexp::IGNORECASE).match?(value)
75
+ end
76
+ end
77
+
78
+ # AND predicate
79
+ class And < Predicate
80
+ attr_reader :left, :right
81
+
82
+ def initialize(left, right)
83
+ super(:and)
84
+ @left = left
85
+ @right = right
86
+ end
87
+
88
+ def evaluate(row)
89
+ @left.evaluate(row) && @right.evaluate(row)
90
+ end
91
+
92
+ def to_s
93
+ "(#{@left}) AND (#{@right})"
94
+ end
95
+ end
96
+
97
+ # OR predicate
98
+ class Or < Predicate
99
+ attr_reader :left, :right
100
+
101
+ def initialize(left, right)
102
+ super(:or)
103
+ @left = left
104
+ @right = right
105
+ end
106
+
107
+ def evaluate(row)
108
+ @left.evaluate(row) || @right.evaluate(row)
109
+ end
110
+
111
+ def to_s
112
+ "(#{@left}) OR (#{@right})"
113
+ end
114
+ end
115
+
116
+ # NOT predicate
117
+ class Not < Predicate
118
+ attr_reader :operand
119
+
120
+ def initialize(operand)
121
+ super(:not)
122
+ @operand = operand
123
+ end
124
+
125
+ def evaluate(row)
126
+ !@operand.evaluate(row)
127
+ end
128
+
129
+ def to_s
130
+ "NOT (#{@operand})"
131
+ end
132
+ end
133
+
134
+ # BETWEEN predicate
135
+ class Between < Predicate
136
+ attr_reader :expression, :low, :high
137
+
138
+ def initialize(expression, low, high)
139
+ super(:between)
140
+ @expression = expression
141
+ @low = low
142
+ @high = high
143
+ end
144
+
145
+ def evaluate(row)
146
+ val = @expression.evaluate(row)
147
+ low_val = @low.evaluate(row)
148
+ high_val = @high.evaluate(row)
149
+
150
+ return false if val.nil? || low_val.nil? || high_val.nil?
151
+
152
+ val >= low_val && val <= high_val
153
+ end
154
+
155
+ def to_s
156
+ "#{@expression} BETWEEN #{@low} AND #{@high}"
157
+ end
158
+ end
159
+
160
+ # IN predicate
161
+ class In < Predicate
162
+ attr_reader :expression, :values
163
+
164
+ def initialize(expression, values)
165
+ super(:in)
166
+ @expression = expression
167
+ @values = values
168
+ end
169
+
170
+ def evaluate(row)
171
+ val = @expression.evaluate(row)
172
+ return false if val.nil?
173
+
174
+ @values.any? { |v| v.evaluate(row) == val }
175
+ end
176
+
177
+ def to_s
178
+ values = @values.map(&:to_s).join(", ")
179
+ "#{@expression} IN (#{values})"
180
+ end
181
+ end
182
+
183
+ # IS NULL predicate
184
+ class IsNull < Predicate
185
+ attr_reader :expression, :negated
186
+
187
+ def initialize(expression, negated = false)
188
+ super(:is_null)
189
+ @expression = expression
190
+ @negated = negated
191
+ end
192
+
193
+ def evaluate(row)
194
+ val = @expression.evaluate(row)
195
+ @negated ? !val.nil? : val.nil?
196
+ end
197
+
198
+ def to_s
199
+ @negated ? "#{@expression} IS NOT NULL" : "#{@expression} IS NULL"
200
+ end
201
+ end
202
+
203
+ # LIKE predicate
204
+ class Like < Predicate
205
+ attr_reader :expression, :pattern, :case_sensitive
206
+
207
+ def initialize(expression, pattern, case_sensitive = true)
208
+ super(:like)
209
+ @expression = expression
210
+ @pattern = pattern
211
+ @case_sensitive = case_sensitive
212
+ end
213
+
214
+ def evaluate(row)
215
+ val = @expression.evaluate(row)
216
+ pat = @pattern.evaluate(row)
217
+
218
+ return false if val.nil? || pat.nil?
219
+
220
+ str = @case_sensitive ? val.to_s : val.to_s.downcase
221
+ pat_str = @case_sensitive ? pat.to_s : pat.to_s.downcase
222
+
223
+ regex_str = Regexp.escape(pat_str)
224
+ .gsub('%', '.*')
225
+ .gsub('_', '.')
226
+ Regexp.new("^#{regex_str}$").match?(str)
227
+ end
228
+
229
+ def to_s
230
+ @case_sensitive ? "#{@expression} LIKE #{@pattern}" : "#{@expression} ILIKE #{@pattern}"
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # Scan - Base class for table scans
6
+ class Scan
7
+ attr_reader :plan, :stats
8
+
9
+ def initialize(plan)
10
+ @plan = plan
11
+ @stats = {
12
+ rows_scanned: 0,
13
+ rows_returned: 0,
14
+ pages_accessed: 0,
15
+ scan_time_ms: 0
16
+ }
17
+ @position = 0
18
+ @results = nil
19
+ @executed = false
20
+ end
21
+
22
+ def execute
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def next
27
+ return nil if @results.nil? || @position >= @results.size
28
+
29
+ result = @results[@position]
30
+ @position += 1
31
+ result
32
+ end
33
+
34
+ def reset
35
+ @position = 0
36
+ @results = nil
37
+ @executed = false
38
+ end
39
+
40
+ def count
41
+ @results ? @results.size : 0
42
+ end
43
+
44
+ def scan_stats
45
+ @stats
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # SequentialScan - Scans a table sequentially
6
+ class SequentialScan < Scan
7
+ attr_reader :engine
8
+
9
+ def initialize(engine, plan)
10
+ super(plan)
11
+ @engine = engine
12
+ @filtered_rows = nil
13
+ end
14
+
15
+ def execute
16
+ start_time = Time.now
17
+
18
+ table_name = @plan.table_name
19
+ columns = @engine.table_columns(table_name)
20
+
21
+ # Get all rows
22
+ rows = @engine.select_rows(table_name, columns)
23
+ @stats[:rows_scanned] = rows.size
24
+ @stats[:pages_accessed] = (rows.size / 100.0).ceil + 1
25
+
26
+ # Apply predicate if present
27
+ if @plan.predicate
28
+ rows = rows.select do |row|
29
+ @plan.predicate.evaluate(row)
30
+ end
31
+ end
32
+
33
+ # Apply projections
34
+ if @plan.projections
35
+ rows = rows.map do |row|
36
+ project_row(row)
37
+ end
38
+ end
39
+
40
+ @results = rows
41
+ @stats[:rows_returned] = rows.size
42
+ @stats[:scan_time_ms] = ((Time.now - start_time) * 1000).round(2)
43
+ @executed = true
44
+
45
+ @results
46
+ end
47
+
48
+ private
49
+
50
+ def project_row(row)
51
+ result = {}
52
+ @plan.projections.each do |proj|
53
+ if proj.is_a?(String) || proj.is_a?(Symbol)
54
+ result[proj.to_s] = row[proj.to_s]
55
+ elsif proj.respond_to?(:evaluate)
56
+ result[proj.alias || proj.to_s] = proj.evaluate(row)
57
+ end
58
+ end
59
+ result
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # SortExecutor - Executes sorting operations
6
+ class SortExecutor
7
+ attr_reader :engine, :stats
8
+
9
+ def initialize(engine)
10
+ @engine = engine
11
+ @stats = {
12
+ sorts: 0,
13
+ rows_sorted: 0,
14
+ total_time_ms: 0
15
+ }
16
+ @lock = Mutex.new
17
+ end
18
+
19
+ def execute(plan, transaction_id = nil)
20
+ @lock.synchronize do
21
+ start_time = Time.now
22
+ @stats[:sorts] += 1
23
+
24
+ # Get input rows
25
+ rows = plan.input_rows || []
26
+ if rows.empty? && plan.input_plan
27
+ executor = Executor.new(@engine)
28
+ result = executor.execute(plan.input_plan, transaction_id)
29
+ rows = result[:rows] if result
30
+ end
31
+
32
+ order_by = plan.order_by || []
33
+
34
+ # Perform sorting
35
+ sorted_rows = sort_rows(rows, order_by)
36
+
37
+ # Apply limit if specified
38
+ if plan.limit
39
+ sorted_rows = sorted_rows.first(plan.limit)
40
+ end
41
+
42
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
43
+ @stats[:rows_sorted] += sorted_rows.size
44
+ @stats[:total_time_ms] += elapsed_ms
45
+
46
+ {
47
+ rows: sorted_rows,
48
+ row_count: sorted_rows.size,
49
+ order_by: order_by,
50
+ message: "SORT produced #{sorted_rows.size} rows"
51
+ }
52
+ end
53
+ end
54
+
55
+ def execute_external_sort(plan, temp_dir = nil, transaction_id = nil)
56
+ @lock.synchronize do
57
+ start_time = Time.now
58
+ @stats[:sorts] += 1
59
+
60
+ # Get input rows
61
+ rows = plan.input_rows || []
62
+ if rows.empty? && plan.input_plan
63
+ executor = Executor.new(@engine)
64
+ result = executor.execute(plan.input_plan, transaction_id)
65
+ rows = result[:rows] if result
66
+ end
67
+
68
+ order_by = plan.order_by || []
69
+
70
+ # External sort for large datasets
71
+ sorted_rows = external_sort(rows, order_by, temp_dir)
72
+
73
+ # Apply limit if specified
74
+ if plan.limit
75
+ sorted_rows = sorted_rows.first(plan.limit)
76
+ end
77
+
78
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
79
+ @stats[:rows_sorted] += sorted_rows.size
80
+ @stats[:total_time_ms] += elapsed_ms
81
+
82
+ {
83
+ rows: sorted_rows,
84
+ row_count: sorted_rows.size,
85
+ order_by: order_by,
86
+ message: "EXTERNAL SORT produced #{sorted_rows.size} rows"
87
+ }
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def sort_rows(rows, order_by)
94
+ return rows if order_by.empty?
95
+
96
+ rows.sort do |a, b|
97
+ comparison = 0
98
+ order_by.each do |order|
99
+ col = order.column.to_s
100
+ val_a = a[col]
101
+ val_b = b[col]
102
+
103
+ comparison = compare_values(val_a, val_b)
104
+ comparison = -comparison if order.direction == :desc
105
+ break unless comparison == 0
106
+ end
107
+ comparison
108
+ end
109
+ end
110
+
111
+ def compare_values(a, b)
112
+ return 0 if a.nil? && b.nil?
113
+ return -1 if a.nil?
114
+ return 1 if b.nil?
115
+
116
+ if a.is_a?(String) && b.is_a?(String)
117
+ a <=> b
118
+ else
119
+ a <=> b
120
+ end
121
+ end
122
+
123
+ def external_sort(rows, order_by, temp_dir)
124
+ # For large datasets, sort in chunks
125
+ chunk_size = 10000
126
+ chunks = []
127
+
128
+ rows.each_slice(chunk_size) do |chunk|
129
+ chunks << sort_rows(chunk, order_by)
130
+ end
131
+
132
+ # Merge sorted chunks
133
+ merge_sorted_chunks(chunks, order_by)
134
+ end
135
+
136
+ def merge_sorted_chunks(chunks, order_by)
137
+ return [] if chunks.empty?
138
+ return chunks.first if chunks.size == 1
139
+
140
+ result = []
141
+ indices = Array.new(chunks.size, 0)
142
+
143
+ loop do
144
+ min_index = nil
145
+ min_value = nil
146
+ min_row = nil
147
+
148
+ chunks.each_with_index do |chunk, idx|
149
+ pos = indices[idx]
150
+ next if pos >= chunk.size
151
+
152
+ row = chunk[pos]
153
+ if min_value.nil? || compare_rows(row, min_row, order_by) < 0
154
+ min_index = idx
155
+ min_value = row
156
+ min_row = row
157
+ end
158
+ end
159
+
160
+ break if min_index.nil?
161
+
162
+ result << min_row
163
+ indices[min_index] += 1
164
+ end
165
+
166
+ result
167
+ end
168
+
169
+ def compare_rows(a, b, order_by)
170
+ order_by.each do |order|
171
+ col = order.column.to_s
172
+ val_a = a[col]
173
+ val_b = b[col]
174
+ comparison = compare_values(val_a, val_b)
175
+ return comparison unless comparison == 0
176
+ end
177
+ 0
178
+ end
179
+
180
+ def stats
181
+ @stats
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # UpdateExecutor - Executes UPDATE operations
6
+ class UpdateExecutor
7
+ attr_reader :engine, :stats
8
+
9
+ def initialize(engine)
10
+ @engine = engine
11
+ @stats = {
12
+ updates: 0,
13
+ rows_updated: 0,
14
+ total_time_ms: 0
15
+ }
16
+ @lock = Mutex.new
17
+ end
18
+
19
+ def execute(plan, transaction_id = nil)
20
+ @lock.synchronize do
21
+ start_time = Time.now
22
+ @stats[:updates] += 1
23
+
24
+ table_name = plan.table_name
25
+ assignments = plan.assignments
26
+ predicate = plan.predicate
27
+
28
+ # Get table columns
29
+ table_columns = @engine.table_columns(table_name)
30
+ raise ExecutionError, "Table '#{table_name}' does not exist" if table_columns.empty?
31
+
32
+ # Find rows to update
33
+ rows = find_rows(table_name, predicate)
34
+
35
+ updated_count = 0
36
+ updated_rows = []
37
+
38
+ rows.each do |row|
39
+ # Get row ID
40
+ row_id = row[:_row_id] || row["_row_id"] || row[:id] || row["id"]
41
+
42
+ # Apply updates
43
+ row_data = row.dup
44
+ assignments.each do |assignment|
45
+ column = assignment.column.to_s
46
+ value = evaluate_expression(assignment.value, row)
47
+ row_data[column] = value
48
+ end
49
+
50
+ # Validate constraints
51
+ validate_constraints(table_name, row_data)
52
+
53
+ # Update in engine
54
+ @engine.update_row(table_name, row_id, row_data, transaction_id: transaction_id)
55
+
56
+ updated_count += 1
57
+ updated_rows << { row_id: row_id, old: row, new: row_data }
58
+ end
59
+
60
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
61
+ @stats[:rows_updated] += updated_count
62
+ @stats[:total_time_ms] += elapsed_ms
63
+
64
+ {
65
+ rows: updated_rows,
66
+ row_count: updated_count,
67
+ row_ids: updated_rows.map { |r| r[:row_id] },
68
+ message: "UPDATE #{updated_count}"
69
+ }
70
+ end
71
+ end
72
+
73
+ def execute_batch(plan, updates, transaction_id = nil)
74
+ @lock.synchronize do
75
+ start_time = Time.now
76
+ @stats[:updates] += 1
77
+
78
+ table_name = plan.table_name
79
+ table_columns = @engine.table_columns(table_name)
80
+
81
+ updated_count = 0
82
+ updated_rows = []
83
+
84
+ updates.each do |update|
85
+ row_id = update[:row_id]
86
+ row_data = update[:values]
87
+
88
+ # Validate constraints
89
+ validate_constraints(table_name, row_data)
90
+
91
+ # Update in engine
92
+ old_row = @engine.select_row(table_name, row_id, table_columns)
93
+ @engine.update_row(table_name, row_id, row_data, transaction_id: transaction_id)
94
+
95
+ updated_count += 1
96
+ updated_rows << { row_id: row_id, old: old_row, new: row_data }
97
+ end
98
+
99
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
100
+ @stats[:rows_updated] += updated_count
101
+ @stats[:total_time_ms] += elapsed_ms
102
+
103
+ {
104
+ rows: updated_rows,
105
+ row_count: updated_count,
106
+ row_ids: updated_rows.map { |r| r[:row_id] },
107
+ message: "UPDATE #{updated_count}"
108
+ }
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def find_rows(table_name, predicate)
115
+ table_columns = @engine.table_columns(table_name)
116
+ rows = @engine.select_rows(table_name, table_columns)
117
+
118
+ if predicate
119
+ rows.select { |row| predicate.evaluate(row) }
120
+ else
121
+ rows
122
+ end
123
+ end
124
+
125
+ def evaluate_expression(expr, row)
126
+ case expr
127
+ when Expression
128
+ expr.evaluate(row)
129
+ when Hash
130
+ expr.transform_values { |v| evaluate_expression(v, row) }
131
+ when Array
132
+ expr.map { |v| evaluate_expression(v, row) }
133
+ else
134
+ expr
135
+ end
136
+ end
137
+
138
+ def validate_constraints(table_name, row_data)
139
+ table_columns = @engine.table_columns(table_name)
140
+ table_columns.each do |col|
141
+ # Check NOT NULL
142
+ if !col.nullable? && row_data[col.name].nil?
143
+ raise ConstraintError, "Column '#{col.name}' cannot be NULL"
144
+ end
145
+
146
+ # Check UNIQUE
147
+ if col.unique?
148
+ conditions = { col.name => row_data[col.name] }
149
+ existing = @engine.select_rows(table_name, table_columns, conditions)
150
+ if existing.any? && existing.first[:_row_id] != row_data[:_row_id]
151
+ raise ConstraintError, "Duplicate value for unique column '#{col.name}'"
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ def stats
158
+ @stats
159
+ end
160
+ end
161
+ end
162
+ end