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,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # Expression - Represent values, columns, and operations
6
+ class Expression
7
+ attr_reader :type, :value, :name, :left, :right, :operator,
8
+ :operand, :alias, :arguments
9
+
10
+ def initialize(type, **attrs)
11
+ @type = type
12
+ @value = attrs[:value]
13
+ @name = attrs[:name]
14
+ @left = attrs[:left]
15
+ @right = attrs[:right]
16
+ @operator = attrs[:operator]
17
+ @operand = attrs[:operand]
18
+ @alias = attrs[:alias]
19
+ @arguments = attrs[:arguments] || []
20
+ end
21
+
22
+ def to_s
23
+ case @type
24
+ when :literal
25
+ @value.to_s
26
+ when :column
27
+ @name.to_s
28
+ when :binary_op
29
+ "#{@left} #{@operator} #{@right}"
30
+ when :unary_op
31
+ "#{@operator} #{@operand}"
32
+ when :function
33
+ "#{@name}(#{@arguments.map(&:to_s).join(', ')})"
34
+ when :parameter
35
+ "?"
36
+ else
37
+ "?"
38
+ end
39
+ end
40
+
41
+ # Literal value
42
+ class Literal < Expression
43
+ def initialize(value)
44
+ super(:literal, value: value)
45
+ end
46
+
47
+ def evaluate(_row = nil)
48
+ @value
49
+ end
50
+
51
+ def to_s
52
+ if @value.nil?
53
+ "NULL"
54
+ elsif @value.is_a?(String)
55
+ "'#{@value}'"
56
+ else
57
+ @value.to_s
58
+ end
59
+ end
60
+ end
61
+
62
+ # Column reference
63
+ class Column < Expression
64
+ attr_reader :table
65
+
66
+ def initialize(name, table = nil)
67
+ super(:column, name: name)
68
+ @table = table
69
+ end
70
+
71
+ def evaluate(row)
72
+ return nil unless row
73
+ return row[@name] if row.key?(@name)
74
+ return row[@name.to_sym] if row.respond_to?(:key?) && row.key?(@name.to_sym)
75
+
76
+ nil
77
+ end
78
+
79
+ def to_s
80
+ @table ? "#{@table}.#{@name}" : @name.to_s
81
+ end
82
+ end
83
+
84
+ # Binary operation
85
+ class BinaryOp < Expression
86
+ def initialize(left, right, operator)
87
+ super(:binary_op, left: left, right: right, operator: operator)
88
+ end
89
+
90
+ def evaluate(row)
91
+ left_val = @left.evaluate(row)
92
+ right_val = @right.evaluate(row)
93
+
94
+ return nil if left_val.nil? || right_val.nil?
95
+
96
+ case @operator
97
+ when :plus then left_val + right_val
98
+ when :minus then left_val - right_val
99
+ when :multiply then left_val * right_val
100
+ when :divide then right_val != 0 ? left_val / right_val : nil
101
+ when :modulo then right_val != 0 ? left_val % right_val : nil
102
+ when :concat then left_val.to_s + right_val.to_s
103
+ when :eq then left_val == right_val
104
+ when :ne then left_val != right_val
105
+ when :lt then left_val < right_val
106
+ when :lte then left_val <= right_val
107
+ when :gt then left_val > right_val
108
+ when :gte then left_val >= right_val
109
+ else nil
110
+ end
111
+ end
112
+
113
+ def to_s
114
+ "#{@left} #{@operator} #{@right}"
115
+ end
116
+ end
117
+
118
+ # Unary operation
119
+ class UnaryOp < Expression
120
+ def initialize(operand, operator)
121
+ super(:unary_op, operand: operand, operator: operator)
122
+ end
123
+
124
+ def evaluate(row)
125
+ val = @operand.evaluate(row)
126
+ return nil if val.nil?
127
+
128
+ case @operator
129
+ when :negate then -val
130
+ when :not then !val
131
+ else val
132
+ end
133
+ end
134
+
135
+ def to_s
136
+ "#{@operator} #{@operand}"
137
+ end
138
+ end
139
+
140
+ # Function call
141
+ class Function < Expression
142
+ def initialize(name, arguments = [], alias_name = nil)
143
+ super(:function, name: name, arguments: arguments, alias: alias_name)
144
+ end
145
+
146
+ def evaluate(row)
147
+ args = @arguments.map { |arg| arg.evaluate(row) }
148
+
149
+ case @name.to_s.upcase
150
+ when "COUNT" then args.compact.size
151
+ when "SUM" then args.compact.sum
152
+ when "AVG"
153
+ vals = args.compact
154
+ vals.empty? ? 0 : vals.sum / vals.size.to_f
155
+ when "MIN" then args.compact.min
156
+ when "MAX" then args.compact.max
157
+ when "LOWER" then args.first.to_s.downcase
158
+ when "UPPER" then args.first.to_s.upcase
159
+ when "LENGTH" then args.first.to_s.length
160
+ when "COALESCE" then args.find { |arg| !arg.nil? }
161
+ when "NOW" then Time.now
162
+ when "CURRENT_DATE" then Date.today
163
+ when "CURRENT_TIME" then Time.now
164
+ else nil
165
+ end
166
+ end
167
+
168
+ def to_s
169
+ args = @arguments.map(&:to_s).join(", ")
170
+ "#{@name}(#{args})"
171
+ end
172
+ end
173
+
174
+ # Parameter placeholder
175
+ class Parameter < Expression
176
+ attr_reader :index
177
+
178
+ def initialize(index = nil)
179
+ super(:parameter, value: index)
180
+ @index = index
181
+ end
182
+
183
+ def evaluate(_row = nil)
184
+ @value
185
+ end
186
+
187
+ def to_s
188
+ @index ? "$#{@index}" : "?"
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # IndexScan - Scans a table using an index
6
+ class IndexScan < Scan
7
+ attr_reader :engine, :index
8
+
9
+ def initialize(engine, plan)
10
+ super(plan)
11
+ @engine = engine
12
+ @index = plan.index
13
+ @index_scan = nil
14
+ end
15
+
16
+ def execute
17
+ start_time = Time.now
18
+
19
+ table_name = @plan.table_name
20
+ columns = @engine.table_columns(table_name)
21
+
22
+ # Create index scan
23
+ @index_scan = Indexes::IndexScan.new(
24
+ @index,
25
+ determine_scan_type,
26
+ build_scan_conditions
27
+ )
28
+
29
+ # Execute index scan
30
+ index_results = @index_scan.execute
31
+ @stats[:rows_scanned] = index_results.size
32
+ @stats[:pages_accessed] = @index_scan.stats[:index_pages_accessed] || 1
33
+
34
+ # Fetch rows by row_id
35
+ rows = []
36
+ index_results.each do |result|
37
+ row_id = result[:row_id]
38
+ row = @engine.select_row(table_name, row_id, columns)
39
+ rows << row if row
40
+ end
41
+
42
+ # Apply predicate if present (additional filtering)
43
+ if @plan.predicate
44
+ rows = rows.select do |row|
45
+ @plan.predicate.evaluate(row)
46
+ end
47
+ end
48
+
49
+ # Apply projections
50
+ if @plan.projections
51
+ rows = rows.map do |row|
52
+ project_row(row)
53
+ end
54
+ end
55
+
56
+ @results = rows
57
+ @stats[:rows_returned] = rows.size
58
+ @stats[:scan_time_ms] = ((Time.now - start_time) * 1000).round(2)
59
+ @executed = true
60
+
61
+ @results
62
+ end
63
+
64
+ private
65
+
66
+ def determine_scan_type
67
+ return :full unless @plan.predicate
68
+
69
+ # Try to determine scan type from predicate
70
+ case @plan.predicate
71
+ when Predicate::Comparison
72
+ case @plan.predicate.operator
73
+ when :eq then :equal
74
+ when :lt, :lte, :gt, :gte then :range
75
+ else :full
76
+ end
77
+ when Predicate::Like
78
+ :prefix
79
+ when Predicate::Between
80
+ :range
81
+ when Predicate::In
82
+ :multi_key
83
+ else
84
+ :full
85
+ end
86
+ end
87
+
88
+ def build_scan_conditions
89
+ return {} unless @plan.predicate
90
+
91
+ case @plan.predicate
92
+ when Predicate::Comparison
93
+ build_comparison_conditions(@plan.predicate)
94
+ when Predicate::Between
95
+ {
96
+ start_key: @plan.predicate.low,
97
+ end_key: @plan.predicate.high
98
+ }
99
+ when Predicate::In
100
+ {
101
+ keys: @plan.predicate.values
102
+ }
103
+ when Predicate::Like
104
+ {
105
+ prefix: @plan.predicate.pattern.value
106
+ }
107
+ else
108
+ {}
109
+ end
110
+ end
111
+
112
+ def build_comparison_conditions(predicate)
113
+ case predicate.operator
114
+ when :eq
115
+ { key: predicate.right.value }
116
+ when :lt
117
+ { end_key: predicate.right.value, inclusive_end: false }
118
+ when :lte
119
+ { end_key: predicate.right.value, inclusive_end: true }
120
+ when :gt
121
+ { start_key: predicate.right.value, inclusive_start: false }
122
+ when :gte
123
+ { start_key: predicate.right.value, inclusive_start: true }
124
+ else
125
+ {}
126
+ end
127
+ end
128
+
129
+ def project_row(row)
130
+ result = {}
131
+ @plan.projections.each do |proj|
132
+ if proj.is_a?(String) || proj.is_a?(Symbol)
133
+ result[proj.to_s] = row[proj.to_s]
134
+ elsif proj.respond_to?(:evaluate)
135
+ result[proj.alias || proj.to_s] = proj.evaluate(row)
136
+ end
137
+ end
138
+ result
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Execution
5
+ # InsertExecutor - Executes INSERT operations
6
+ class InsertExecutor
7
+ attr_reader :engine, :stats
8
+
9
+ def initialize(engine)
10
+ @engine = engine
11
+ @stats = {
12
+ inserts: 0,
13
+ rows_inserted: 0,
14
+ conflicts: 0,
15
+ total_time_ms: 0
16
+ }
17
+ @lock = Mutex.new
18
+ end
19
+
20
+ def execute(plan, transaction_id = nil)
21
+ @lock.synchronize do
22
+ start_time = Time.now
23
+ @stats[:inserts] += 1
24
+
25
+ table_name = plan.table_name
26
+ columns = plan.columns
27
+ values = plan.values
28
+
29
+ # Get table columns
30
+ table_columns = @engine.table_columns(table_name)
31
+ raise ExecutionError, "Table '#{table_name}' does not exist" if table_columns.empty?
32
+
33
+ # Handle different value formats
34
+ rows_to_insert = prepare_rows(columns, values, table_columns)
35
+
36
+ # Check constraints
37
+ validate_constraints(table_name, rows_to_insert)
38
+
39
+ # Check for conflicts (if ON CONFLICT specified)
40
+ conflict_action = plan.options[:on_conflict] || :error
41
+ conflict_handled = false
42
+
43
+ if conflict_action != :error && plan.conflict_columns
44
+ rows_to_insert = handle_conflicts(table_name, rows_to_insert, conflict_action, plan.conflict_columns)
45
+ conflict_handled = true
46
+ end
47
+
48
+ # Insert rows
49
+ inserted_rows = []
50
+ rows_to_insert.each do |row_data|
51
+ row_id = @engine.insert_row(table_name, table_columns, row_data)
52
+
53
+ # Update indexes
54
+ if @engine.respond_to?(:index_manager)
55
+ row = row_data.merge("_row_id" => row_id)
56
+ @engine.index_manager.insert_row(table_name, row)
57
+ end
58
+
59
+ inserted_rows << { row_id: row_id, row: row_data }
60
+ end
61
+
62
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
63
+ @stats[:rows_inserted] += inserted_rows.size
64
+ @stats[:total_time_ms] += elapsed_ms
65
+
66
+ {
67
+ rows: inserted_rows,
68
+ row_count: inserted_rows.size,
69
+ row_ids: inserted_rows.map { |r| r[:row_id] },
70
+ message: "INSERT #{inserted_rows.size}",
71
+ conflict_handled: conflict_handled
72
+ }
73
+ end
74
+ end
75
+
76
+ def execute_batch(plan, rows, transaction_id = nil)
77
+ @lock.synchronize do
78
+ start_time = Time.now
79
+ @stats[:inserts] += 1
80
+
81
+ table_name = plan.table_name
82
+ table_columns = @engine.table_columns(table_name)
83
+ raise ExecutionError, "Table '#{table_name}' does not exist" if table_columns.empty?
84
+
85
+ prepared_rows = rows.map do |row|
86
+ prepare_single_row(plan.columns, row, table_columns)
87
+ end
88
+
89
+ # Bulk insert
90
+ inserted_rows = []
91
+ prepared_rows.each do |row_data|
92
+ row_id = @engine.insert_row(table_name, table_columns, row_data)
93
+
94
+ if @engine.respond_to?(:index_manager)
95
+ row = row_data.merge("_row_id" => row_id)
96
+ @engine.index_manager.insert_row(table_name, row)
97
+ end
98
+
99
+ inserted_rows << { row_id: row_id, row: row_data }
100
+ end
101
+
102
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
103
+ @stats[:rows_inserted] += inserted_rows.size
104
+ @stats[:total_time_ms] += elapsed_ms
105
+
106
+ {
107
+ rows: inserted_rows,
108
+ row_count: inserted_rows.size,
109
+ row_ids: inserted_rows.map { |r| r[:row_id] },
110
+ message: "INSERT #{inserted_rows.size}"
111
+ }
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def prepare_rows(columns, values, table_columns)
118
+ return [] if values.empty?
119
+
120
+ # If values is a single array, treat as one row
121
+ if values.first.is_a?(Array) || values.first.is_a?(Hash)
122
+ values.map { |v| prepare_single_row(columns, v, table_columns) }
123
+ else
124
+ [prepare_single_row(columns, values, table_columns)]
125
+ end
126
+ end
127
+
128
+ def prepare_single_row(columns, values, table_columns)
129
+ row_data = {}
130
+
131
+ if values.is_a?(Hash)
132
+ # Values provided as hash
133
+ values.each do |key, val|
134
+ row_data[key.to_s] = val
135
+ end
136
+ elsif values.is_a?(Array)
137
+ # Values provided as array - match by position
138
+ columns.each_with_index do |col, idx|
139
+ row_data[col.to_s] = values[idx] if idx < values.size
140
+ end
141
+ else
142
+ # Single value - use first column
143
+ row_data[columns.first.to_s] = values if columns.any?
144
+ end
145
+
146
+ # Add default values for missing columns
147
+ table_columns.each do |col|
148
+ unless row_data.key?(col.name)
149
+ row_data[col.name] = col.default if col.has_default?
150
+ end
151
+ end
152
+
153
+ row_data
154
+ end
155
+
156
+ def validate_constraints(table_name, rows)
157
+ # Check NOT NULL constraints
158
+ table_columns = @engine.table_columns(table_name)
159
+ rows.each do |row|
160
+ table_columns.each do |col|
161
+ if !col.nullable? && row[col.name].nil?
162
+ raise ConstraintError, "Column '#{col.name}' cannot be NULL"
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ def handle_conflicts(table_name, rows, conflict_action, conflict_columns)
169
+ handled_rows = []
170
+ conflict_columns = conflict_columns.map(&:to_s)
171
+
172
+ rows.each do |row|
173
+ # Check if row conflicts with existing data
174
+ conflict = find_conflict(table_name, row, conflict_columns)
175
+
176
+ if conflict
177
+ @stats[:conflicts] += 1
178
+ if conflict_action == :ignore
179
+ next
180
+ elsif conflict_action == :update
181
+ # Update existing row with new values
182
+ update_conflict_row(table_name, conflict, row)
183
+ next
184
+ end
185
+ end
186
+
187
+ handled_rows << row
188
+ end
189
+
190
+ handled_rows
191
+ end
192
+
193
+ def find_conflict(table_name, row, conflict_columns)
194
+ # Check if any row exists with the same conflicting columns
195
+ conditions = {}
196
+ conflict_columns.each do |col|
197
+ conditions[col] = row[col] if row.key?(col)
198
+ end
199
+
200
+ return nil if conditions.empty?
201
+
202
+ table_columns = @engine.table_columns(table_name)
203
+ rows = @engine.select_rows(table_name, table_columns, conditions)
204
+ rows.first
205
+ end
206
+
207
+ def update_conflict_row(table_name, existing_row, new_row)
208
+ row_id = existing_row[:_row_id] || existing_row["_row_id"]
209
+ @engine.update_row(table_name, row_id, new_row)
210
+ end
211
+
212
+ def stats
213
+ @stats
214
+ end
215
+ end
216
+ end
217
+ end