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,604 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # Base class for all expression nodes
7
+ class Expression < Node
8
+ # Type of expression result (set by type checker)
9
+ attr_accessor :type
10
+
11
+ def initialize(location: nil)
12
+ super(location: location)
13
+ @type = nil
14
+ end
15
+
16
+ # Is this expression a literal value?
17
+ def literal?
18
+ false
19
+ end
20
+
21
+ # Is this expression a column reference?
22
+ def column?
23
+ false
24
+ end
25
+
26
+ # Is this expression NULL?
27
+ def null?
28
+ false
29
+ end
30
+
31
+ # Does this expression contain a subquery?
32
+ def contains_subquery?
33
+ false
34
+ end
35
+ end
36
+
37
+ # Literal value
38
+ class Literal < Expression
39
+ attr_reader :value, :token_type
40
+
41
+ def initialize(value, token_type, location: nil)
42
+ super(location: location)
43
+ @value = value
44
+ @token_type = token_type
45
+ end
46
+
47
+ def literal?
48
+ true
49
+ end
50
+
51
+ def null?
52
+ @value.nil?
53
+ end
54
+
55
+ def accept(visitor)
56
+ visitor.visit_literal(self)
57
+ end
58
+
59
+ def clone
60
+ Literal.new(@value, @token_type, location: @location)
61
+ end
62
+
63
+ def to_sql
64
+ case @token_type
65
+ when Token::Type::STRING
66
+ "'#{@value.to_s.gsub("'", "''")}'"
67
+ when Token::Type::NUMBER
68
+ @value.to_s
69
+ when Token::Type::NULL
70
+ "NULL"
71
+ when Token::Type::TRUE
72
+ "TRUE"
73
+ when Token::Type::FALSE
74
+ "FALSE"
75
+ else
76
+ @value.to_s
77
+ end
78
+ end
79
+
80
+ def inspect
81
+ "Literal(#{@value})"
82
+ end
83
+ end
84
+
85
+ # NULL literal
86
+ class NullLiteral < Literal
87
+ def initialize(location: nil)
88
+ super(nil, Token::Type::NULL, location: location)
89
+ end
90
+
91
+ def null?
92
+ true
93
+ end
94
+
95
+ def to_sql
96
+ "NULL"
97
+ end
98
+
99
+ def inspect
100
+ "NULL"
101
+ end
102
+ end
103
+
104
+ # Column reference
105
+ class Identifier < Expression
106
+ attr_reader :name, :table
107
+
108
+ def initialize(name, table: nil, location: nil)
109
+ super(location: location)
110
+ @name = name
111
+ @table = table
112
+ end
113
+
114
+ def column?
115
+ true
116
+ end
117
+
118
+ def accept(visitor)
119
+ visitor.visit_identifier(self)
120
+ end
121
+
122
+ def clone
123
+ Identifier.new(@name, table: @table, location: @location)
124
+ end
125
+
126
+ def to_sql
127
+ if @table
128
+ "#{@table}.#{@name}"
129
+ else
130
+ @name
131
+ end
132
+ end
133
+
134
+ def inspect
135
+ if @table
136
+ "Identifier(#{@table}.#{@name})"
137
+ else
138
+ "Identifier(#{@name})"
139
+ end
140
+ end
141
+ end
142
+
143
+ # Unary operator expression
144
+ class UnaryOp < Expression
145
+ attr_reader :operator, :operand
146
+
147
+ def initialize(operator, operand, location: nil)
148
+ super(location: location)
149
+ @operator = operator
150
+ @operand = operand
151
+ end
152
+
153
+ def accept(visitor)
154
+ visitor.visit_unary_op(self)
155
+ end
156
+
157
+ def clone
158
+ UnaryOp.new(@operator, @operand.clone, location: @location)
159
+ end
160
+
161
+ def to_sql
162
+ "#{operator_string(@operator)}#{@operand.to_sql}"
163
+ end
164
+
165
+ def inspect
166
+ "UnaryOp(#{@operator}, #{@operand.inspect})"
167
+ end
168
+
169
+ private
170
+
171
+ def operator_string(op)
172
+ case op
173
+ when Token::Type::PLUS then "+"
174
+ when Token::Type::MINUS then "-"
175
+ when Token::Type::TILDE then "~"
176
+ when Token::Type::NOT then "NOT "
177
+ else op.to_s
178
+ end
179
+ end
180
+ end
181
+
182
+ # Binary operator expression
183
+ class BinaryOp < Expression
184
+ attr_reader :operator, :left, :right
185
+
186
+ def initialize(operator, left, right, location: nil)
187
+ super(location: location)
188
+ @operator = operator
189
+ @left = left
190
+ @right = right
191
+ end
192
+
193
+ def accept(visitor)
194
+ visitor.visit_binary_op(self)
195
+ end
196
+
197
+ def clone
198
+ BinaryOp.new(@operator, @left.clone, @right.clone, location: @location)
199
+ end
200
+
201
+ def to_sql
202
+ "#{@left.to_sql} #{operator_string(@operator)} #{@right.to_sql}"
203
+ end
204
+
205
+ def inspect
206
+ "BinaryOp(#{@operator}, #{@left.inspect}, #{@right.inspect})"
207
+ end
208
+
209
+ private
210
+
211
+ def operator_string(op)
212
+ case op
213
+ when Token::Type::EQ then "="
214
+ when Token::Type::NE then "!="
215
+ when Token::Type::LT then "<"
216
+ when Token::Type::LTE then "<="
217
+ when Token::Type::GT then ">"
218
+ when Token::Type::GTE then ">="
219
+ when Token::Type::PLUS then "+"
220
+ when Token::Type::MINUS then "-"
221
+ when Token::Type::STAR then "*"
222
+ when Token::Type::SLASH then "/"
223
+ when Token::Type::PERCENT then "%"
224
+ when Token::Type::AND then "AND"
225
+ when Token::Type::OR then "OR"
226
+ when Token::Type::LIKE then "LIKE"
227
+ when Token::Type::ILIKE then "ILIKE"
228
+ else op.to_s
229
+ end
230
+ end
231
+ end
232
+
233
+ # BETWEEN expression
234
+ class Between < Expression
235
+ attr_reader :expression, :low, :high
236
+
237
+ def initialize(expression, low, high, location: nil)
238
+ super(location: location)
239
+ @expression = expression
240
+ @low = low
241
+ @high = high
242
+ end
243
+
244
+ def accept(visitor)
245
+ visitor.visit_between(self)
246
+ end
247
+
248
+ def clone
249
+ Between.new(@expression.clone, @low.clone, @high.clone, location: @location)
250
+ end
251
+
252
+ def to_sql
253
+ "#{@expression.to_sql} BETWEEN #{@low.to_sql} AND #{@high.to_sql}"
254
+ end
255
+
256
+ def inspect
257
+ "Between(#{@expression.inspect}, #{@low.inspect}, #{@high.inspect})"
258
+ end
259
+ end
260
+
261
+ # IN expression
262
+ class In < Expression
263
+ attr_reader :expression, :values
264
+
265
+ def initialize(expression, values, location: nil)
266
+ super(location: location)
267
+ @expression = expression
268
+ @values = values
269
+ end
270
+
271
+ def accept(visitor)
272
+ visitor.visit_in(self)
273
+ end
274
+
275
+ def clone
276
+ In.new(@expression.clone, @values.map(&:clone), location: @location)
277
+ end
278
+
279
+ def to_sql
280
+ values_sql = @values.map(&:to_sql).join(", ")
281
+ "#{@expression.to_sql} IN (#{values_sql})"
282
+ end
283
+
284
+ def inspect
285
+ "In(#{@expression.inspect}, #{@values.map(&:inspect).join(", ")})"
286
+ end
287
+ end
288
+
289
+ class Subquery < Expression
290
+ attr_reader :query
291
+ def initialize(query, location: nil) = (super(location: location); @query = query)
292
+ def contains_subquery? = true
293
+ def accept(visitor) = visitor.visit_subquery(self)
294
+ def clone = Subquery.new(@query.clone, location: @location)
295
+ def to_sql = "(#{@query.to_sql})"
296
+ end
297
+
298
+ class Exists < Expression
299
+ attr_reader :query
300
+ def initialize(query, location: nil) = (super(location: location); @query = query)
301
+ def accept(visitor) = visitor.visit_exists(self)
302
+ def clone = Exists.new(@query.clone, location: @location)
303
+ def to_sql = "EXISTS (#{@query.to_sql})"
304
+ end
305
+
306
+ # IS NULL expression
307
+ class IsNull < Expression
308
+ attr_reader :expression, :negated
309
+
310
+ def initialize(expression, negated = false, location: nil)
311
+ super(location: location)
312
+ @expression = expression
313
+ @negated = negated
314
+ end
315
+
316
+ def accept(visitor)
317
+ visitor.visit_is_null(self)
318
+ end
319
+
320
+ def clone
321
+ IsNull.new(@expression.clone, @negated, location: @location)
322
+ end
323
+
324
+ def to_sql
325
+ if @negated
326
+ "#{@expression.to_sql} IS NOT NULL"
327
+ else
328
+ "#{@expression.to_sql} IS NULL"
329
+ end
330
+ end
331
+
332
+ def inspect
333
+ if @negated
334
+ "IsNull(#{@expression.inspect}, negated: true)"
335
+ else
336
+ "IsNull(#{@expression.inspect})"
337
+ end
338
+ end
339
+ end
340
+
341
+ # Function call
342
+ class FunctionCall < Expression
343
+ attr_reader :name, :arguments, :distinct, :window
344
+
345
+ def initialize(name, arguments = [], distinct: false, window: nil, location: nil)
346
+ super(location: location)
347
+ @name = name
348
+ @arguments = arguments
349
+ @distinct = distinct
350
+ @window = window
351
+ end
352
+
353
+ def accept(visitor)
354
+ visitor.visit_function_call(self)
355
+ end
356
+
357
+ def clone
358
+ cloned_window = if @window
359
+ {
360
+ partition_by: @window[:partition_by].map(&:clone),
361
+ order_by: @window[:order_by].map(&:clone),
362
+ frame: @window[:frame]&.transform_values { |value| value.is_a?(Hash) ? value.dup : value }
363
+ }
364
+ end
365
+ FunctionCall.new(@name, @arguments.map(&:clone), distinct: @distinct, window: cloned_window, location: @location)
366
+ end
367
+
368
+ def to_sql
369
+ args = @arguments.map(&:to_sql).join(", ")
370
+ args = "DISTINCT #{args}" if @distinct
371
+ sql = "#{@name}(#{args})"
372
+ if @window
373
+ partition = @window[:partition_by]
374
+ ordering = @window[:order_by]
375
+ parts = []
376
+ parts << "PARTITION BY #{partition.map(&:to_sql).join(', ')}" unless partition.empty?
377
+ parts << "ORDER BY #{ordering.map(&:to_sql).join(', ')}" unless ordering.empty?
378
+ parts << "ROWS #{window_frame_sql(@window[:frame])}" if @window[:frame]
379
+ sql << " OVER (#{parts.join(' ')})"
380
+ end
381
+ sql
382
+ end
383
+
384
+ def window_frame_sql(frame)
385
+ boundary = lambda do |value|
386
+ case value[:kind]
387
+ when :unbounded_preceding then "UNBOUNDED PRECEDING"
388
+ when :unbounded_following then "UNBOUNDED FOLLOWING"
389
+ when :current_row then "CURRENT ROW"
390
+ when :preceding then "#{value[:value]} PRECEDING"
391
+ when :following then "#{value[:value]} FOLLOWING"
392
+ else raise ArgumentError, "Unknown window frame boundary: #{value[:kind]}"
393
+ end
394
+ end
395
+ if frame[:start] && frame[:finish]
396
+ "BETWEEN #{boundary.call(frame[:start])} AND #{boundary.call(frame[:finish])}"
397
+ else
398
+ boundary.call(frame[:start])
399
+ end
400
+ end
401
+
402
+ def inspect
403
+ "FunctionCall(#{@name}, #{@arguments.map(&:inspect).join(", ")})"
404
+ end
405
+ end
406
+
407
+ # Parameter (placeholder) for prepared statements
408
+ class Parameter < Expression
409
+ attr_reader :index
410
+
411
+ def initialize(index = nil, location: nil)
412
+ super(location: location)
413
+ @index = index
414
+ end
415
+
416
+ def accept(visitor)
417
+ visitor.visit_parameter(self)
418
+ end
419
+
420
+ def clone
421
+ Parameter.new(@index, location: @location)
422
+ end
423
+
424
+ def to_sql
425
+ if @index
426
+ "$#{@index}"
427
+ else
428
+ "?"
429
+ end
430
+ end
431
+
432
+ def inspect
433
+ if @index
434
+ "Parameter($#{@index})"
435
+ else
436
+ "Parameter(?)"
437
+ end
438
+ end
439
+ end
440
+
441
+ # Star (*) in SELECT
442
+ class Star < Expression
443
+ attr_reader :table
444
+
445
+ def initialize(table: nil, location: nil)
446
+ super(location: location)
447
+ @table = table
448
+ end
449
+
450
+ def accept(visitor)
451
+ visitor.visit_star(self)
452
+ end
453
+
454
+ def clone
455
+ Star.new(table: @table, location: @location)
456
+ end
457
+
458
+ def to_sql
459
+ @table ? "#{@table}.*" : "*"
460
+ end
461
+
462
+ def inspect
463
+ @table ? "Star(#{@table}.*)" : "Star"
464
+ end
465
+ end
466
+
467
+ # SELECT column with optional alias
468
+ class SelectColumn < Node
469
+ attr_reader :expression, :alias_name
470
+
471
+ def initialize(expression, alias_name = nil, location: nil)
472
+ super(location: location)
473
+ @expression = expression
474
+ @alias_name = alias_name
475
+ end
476
+
477
+ def accept(visitor)
478
+ visitor.visit_select_column(self)
479
+ end
480
+
481
+ def clone
482
+ SelectColumn.new(@expression.clone, @alias_name, location: @location)
483
+ end
484
+
485
+ def to_sql
486
+ if @alias_name
487
+ "#{@expression.to_sql} AS #{@alias_name}"
488
+ else
489
+ @expression.to_sql
490
+ end
491
+ end
492
+
493
+ def inspect
494
+ if @alias_name
495
+ "SelectColumn(#{@expression.inspect}, AS #{@alias_name})"
496
+ else
497
+ "SelectColumn(#{@expression.inspect})"
498
+ end
499
+ end
500
+ end
501
+
502
+ # Table reference with optional alias
503
+ class TableRef < Node
504
+ attr_reader :name, :alias_name
505
+
506
+ def initialize(name, alias_name = nil, location: nil)
507
+ super(location: location)
508
+ @name = name
509
+ @alias_name = alias_name
510
+ end
511
+
512
+ def accept(visitor)
513
+ visitor.visit_table_ref(self)
514
+ end
515
+
516
+ def clone
517
+ TableRef.new(@name, @alias_name, location: @location)
518
+ end
519
+
520
+ def to_sql
521
+ if @alias_name
522
+ "#{@name} AS #{@alias_name}"
523
+ else
524
+ @name
525
+ end
526
+ end
527
+
528
+ def inspect
529
+ if @alias_name
530
+ "TableRef(#{@name} AS #{@alias_name})"
531
+ else
532
+ "TableRef(#{@name})"
533
+ end
534
+ end
535
+ end
536
+
537
+ # A joined table and its ON predicate. JOIN support is intentionally
538
+ # represented in the regular SELECT AST so parser, planner and executor
539
+ # all agree on table aliases and qualified identifiers.
540
+ class Join < Node
541
+ attr_reader :type, :table, :condition
542
+
543
+ def initialize(type, table, condition = nil, location: nil)
544
+ super(location: location)
545
+ @type = type
546
+ @table = table
547
+ @condition = condition
548
+ end
549
+
550
+ def accept(visitor)
551
+ visitor.visit_join(self)
552
+ end
553
+
554
+ def clone
555
+ Join.new(@type, @table.clone, @condition&.clone, location: @location)
556
+ end
557
+
558
+ def to_sql
559
+ keyword = {
560
+ left: "LEFT JOIN",
561
+ right: "RIGHT JOIN",
562
+ full: "FULL JOIN",
563
+ cross: "CROSS JOIN"
564
+ }.fetch(@type, "INNER JOIN")
565
+ sql = "#{keyword} #{@table.to_sql}"
566
+ sql << " ON #{@condition.to_sql}" if @condition
567
+ sql
568
+ end
569
+
570
+ def inspect
571
+ "Join(#{@type}, #{@table.inspect}, #{@condition.inspect})"
572
+ end
573
+ end
574
+
575
+ # ORDER BY item
576
+ class OrderItem < Node
577
+ attr_reader :expression, :direction
578
+
579
+ def initialize(expression, direction = :asc, location: nil)
580
+ super(location: location)
581
+ @expression = expression
582
+ @direction = direction
583
+ end
584
+
585
+ def accept(visitor)
586
+ visitor.visit_order_item(self)
587
+ end
588
+
589
+ def clone
590
+ OrderItem.new(@expression.clone, @direction, location: @location)
591
+ end
592
+
593
+ def to_sql
594
+ dir = @direction == :asc ? "ASC" : "DESC"
595
+ "#{@expression.to_sql} #{dir}"
596
+ end
597
+
598
+ def inspect
599
+ "OrderItem(#{@expression.inspect}, #{@direction})"
600
+ end
601
+ end
602
+ end
603
+ end
604
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # INSERT statement AST node
7
+ class Insert < Node
8
+ attr_reader :table, :columns, :values, :rows, :on_conflict
9
+
10
+ def initialize(table, columns = [], values = [], rows: nil, on_conflict: nil, location: nil)
11
+ super(location: location)
12
+ @table = table
13
+ @columns = columns
14
+ @rows = rows || [values]
15
+ @values = @rows.first || []
16
+ @on_conflict = on_conflict
17
+ end
18
+
19
+ def accept(visitor)
20
+ visitor.visit_insert(self)
21
+ end
22
+
23
+ def clone
24
+ Insert.new(
25
+ @table,
26
+ @columns.dup,
27
+ @values.map(&:clone), rows: @rows.map { |row| row.map(&:clone) }, on_conflict: @on_conflict,
28
+ location: @location
29
+ )
30
+ end
31
+
32
+ def to_sql
33
+ parts = []
34
+ parts << "INSERT INTO #{@table}"
35
+
36
+ if @columns.any?
37
+ parts << "(#{@columns.join(", ")})"
38
+ end
39
+
40
+ parts << "VALUES"
41
+ parts << @rows.map { |row| "(#{row.map(&:to_sql).join(", ")})" }.join(", ")
42
+ parts << "ON CONFLICT DO NOTHING" if @on_conflict == :nothing
43
+ if @on_conflict.is_a?(Hash) && @on_conflict[:action] == :update
44
+ target = @on_conflict[:target].any? ? " (#{@on_conflict[:target].join(', ')})" : ""
45
+ assignments = @on_conflict[:assignments].map(&:to_sql).join(", ")
46
+ parts << "ON CONFLICT#{target} DO UPDATE SET #{assignments}"
47
+ end
48
+
49
+ parts.join(" ")
50
+ end
51
+
52
+ def inspect
53
+ cols = @columns.any? ? @columns.join(", ") : "ALL"
54
+ vals = @rows.map { |row| "(#{row.map(&:inspect).join(", ")})" }.join(", ")
55
+ "Insert(table: #{@table}, columns: [#{cols}], values: [#{vals}])"
56
+ end
57
+
58
+ # Helper methods
59
+ def has_columns?
60
+ @columns.any?
61
+ end
62
+
63
+ def value_count
64
+ @values.size
65
+ end
66
+
67
+ def row_count
68
+ @rows.size
69
+ end
70
+
71
+ def column_count
72
+ @columns.size
73
+ end
74
+
75
+ # Get column index by name
76
+ def column_index(name)
77
+ @columns.index(name)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module AST
6
+ # Base class for all AST nodes
7
+ class Node
8
+ attr_reader :location
9
+
10
+ def initialize(location: nil)
11
+ @location = location
12
+ end
13
+
14
+ # Accept a visitor for traversal
15
+ def accept(visitor)
16
+ raise NotImplementedError, "#{self.class} must implement #accept"
17
+ end
18
+
19
+ # Deep clone of the node
20
+ def clone
21
+ raise NotImplementedError, "#{self.class} must implement #clone"
22
+ end
23
+
24
+ # Convert to SQL string
25
+ def to_sql
26
+ raise NotImplementedError, "#{self.class} must implement #to_sql"
27
+ end
28
+
29
+ # Compare two nodes for equality
30
+ def ==(other)
31
+ self.class == other.class && instance_variables.all? do |ivar|
32
+ instance_variable_get(ivar) == other.instance_variable_get(ivar)
33
+ end
34
+ end
35
+
36
+ def inspect
37
+ "#<#{self.class}>"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end