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,550 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ module Planner
6
+ # Binder - Resolves identifiers and binds AST nodes to catalog objects
7
+ class Binder
8
+ attr_reader :catalog, :errors, :warnings
9
+
10
+ def initialize(catalog)
11
+ @catalog = catalog
12
+ @errors = []
13
+ @warnings = []
14
+ @current_scope = {}
15
+ @current_table = nil
16
+ @current_table_alias = nil
17
+ @in_subquery = false
18
+ end
19
+
20
+ # Bind an AST statement to catalog objects
21
+ def bind(statement)
22
+ @errors = []
23
+ @warnings = []
24
+ statement.accept(self)
25
+ [statement, @errors, @warnings]
26
+ end
27
+
28
+ # --- Visitor methods ---
29
+
30
+ def visit_select(node)
31
+ # Bind FROM clause first
32
+ if node.from
33
+ table_info = bind_table_reference(node.from)
34
+ @current_table = table_info
35
+ @current_table_alias = node.from.alias_name || node.from.name
36
+
37
+ # Build scope from table columns
38
+ @current_scope = build_scope_from_table(table_info)
39
+ end
40
+
41
+ # Bind WHERE clause
42
+ if node.where
43
+ node.where.accept(self)
44
+ end
45
+
46
+ # Bind ORDER BY
47
+ node.order_by.each do |order_item|
48
+ order_item.accept(self)
49
+ end
50
+
51
+ # Bind LIMIT and OFFSET
52
+ node.limit&.accept(self)
53
+ node.offset&.accept(self)
54
+
55
+ # Bind columns
56
+ node.columns.each do |col|
57
+ col.accept(self)
58
+ end
59
+
60
+ # Clear scope after binding
61
+ @current_scope = {}
62
+ @current_table = nil
63
+ @current_table_alias = nil
64
+ end
65
+
66
+ def visit_insert(node)
67
+ table_info = @catalog.find_table(node.table)
68
+ unless table_info
69
+ @errors << "Table '#{node.table}' does not exist"
70
+ return
71
+ end
72
+
73
+ @current_table = table_info
74
+ @current_table_alias = node.table
75
+ @current_scope = build_scope_from_table(table_info)
76
+
77
+ # Validate columns
78
+ if node.columns.any?
79
+ node.columns.each do |col_name|
80
+ unless table_info.columns.any? { |c| c.name == col_name }
81
+ @errors << "Column '#{col_name}' does not exist in table '#{node.table}'"
82
+ end
83
+ end
84
+ end
85
+
86
+ # Bind values
87
+ node.rows.each do |row|
88
+ row.each do |value|
89
+ value.accept(self)
90
+ end
91
+ end
92
+
93
+ @current_scope = {}
94
+ @current_table = nil
95
+ @current_table_alias = nil
96
+ end
97
+
98
+ def visit_update(node)
99
+ table_info = @catalog.find_table(node.table)
100
+ unless table_info
101
+ @errors << "Table '#{node.table}' does not exist"
102
+ return
103
+ end
104
+
105
+ @current_table = table_info
106
+ @current_table_alias = node.table
107
+ @current_scope = build_scope_from_table(table_info)
108
+
109
+ # Validate assignments
110
+ node.assignments.each do |assignment|
111
+ unless table_info.columns.any? { |c| c.name == assignment.column }
112
+ @errors << "Column '#{assignment.column}' does not exist in table '#{node.table}'"
113
+ end
114
+ assignment.value.accept(self)
115
+ end
116
+
117
+ # Bind WHERE clause
118
+ if node.where
119
+ node.where.accept(self)
120
+ end
121
+
122
+ @current_scope = {}
123
+ @current_table = nil
124
+ @current_table_alias = nil
125
+ end
126
+
127
+ def visit_delete(node)
128
+ table_info = @catalog.find_table(node.table)
129
+ unless table_info
130
+ @errors << "Table '#{node.table}' does not exist"
131
+ return
132
+ end
133
+
134
+ @current_table = table_info
135
+ @current_table_alias = node.table
136
+ @current_scope = build_scope_from_table(table_info)
137
+
138
+ # Bind WHERE clause
139
+ if node.where
140
+ node.where.accept(self)
141
+ end
142
+
143
+ @current_scope = {}
144
+ @current_table = nil
145
+ @current_table_alias = nil
146
+ end
147
+
148
+ def visit_create_table(node)
149
+ # Check if table already exists
150
+ if @catalog.find_table(node.name)
151
+ unless node.if_not_exists
152
+ @errors << "Table '#{node.name}' already exists"
153
+ end
154
+ return
155
+ end
156
+
157
+ # Validate column definitions
158
+ node.columns.each do |col|
159
+ # Check for duplicate column names
160
+ if node.columns.count { |c| c.name == col.name } > 1
161
+ @errors << "Duplicate column name '#{col.name}' in table '#{node.name}'"
162
+ end
163
+
164
+ # Validate type
165
+ type_class = col.type_class
166
+ unless valid_type?(type_class)
167
+ @errors << "Invalid data type '#{type_class}' for column '#{col.name}'"
168
+ end
169
+ end
170
+
171
+ # Validate constraints
172
+ node.constraints.each do |constraint|
173
+ constraint.accept(self)
174
+ end
175
+ end
176
+
177
+ def visit_alter_table_add_column(node)
178
+ table_info = @catalog.find_table(node.table_name)
179
+ unless table_info
180
+ @errors << "Table '#{node.table_name}' does not exist"
181
+ return
182
+ end
183
+
184
+ # Check if column already exists
185
+ if table_info.columns.any? { |c| c.name == node.column_name }
186
+ @errors << "Column '#{node.column_name}' already exists in table '#{node.table_name}'"
187
+ end
188
+
189
+ # Validate type
190
+ type_class = node.column_type.is_a?(Hash) ? node.column_type[:type] : node.column_type
191
+ unless valid_type?(type_class)
192
+ @errors << "Invalid data type '#{type_class}' for column '#{node.column_name}'"
193
+ end
194
+ end
195
+
196
+ def visit_alter_table_drop_column(node)
197
+ table_info = @catalog.find_table(node.table_name)
198
+ unless table_info
199
+ @errors << "Table '#{node.table_name}' does not exist"
200
+ return
201
+ end
202
+
203
+ unless table_info.columns.any? { |c| c.name == node.column_name }
204
+ @errors << "Column '#{node.column_name}' does not exist in table '#{node.table_name}'"
205
+ end
206
+ end
207
+
208
+ def visit_alter_table_add_constraint(node)
209
+ table_info = @catalog.find_table(node.table_name)
210
+ unless table_info
211
+ @errors << "Table '#{node.table_name}' does not exist"
212
+ return
213
+ end
214
+ @current_table = table_info
215
+ node.constraint.accept(self)
216
+ @current_table = nil
217
+ end
218
+
219
+ def visit_alter_table_drop_constraint(node)
220
+ table_info = @catalog.find_table(node.table_name)
221
+ unless table_info
222
+ @errors << "Table '#{node.table_name}' does not exist"
223
+ return
224
+ end
225
+ # Constraint metadata is persisted by the storage engine and may not
226
+ # yet be hydrated into the catalog after a restart. The executor is
227
+ # authoritative for the existence check.
228
+ end
229
+
230
+ def visit_drop_table(node)
231
+ unless @catalog.find_table(node.name)
232
+ unless node.if_exists
233
+ @errors << "Table '#{node.name}' does not exist"
234
+ end
235
+ end
236
+ end
237
+
238
+ def visit_create_index(node)
239
+ table_info = @catalog.find_table(node.table_name)
240
+ unless table_info
241
+ @errors << "Table '#{node.table_name}' does not exist"
242
+ return
243
+ end
244
+
245
+ # Validate columns
246
+ node.columns.each do |col_name|
247
+ unless table_info.columns.any? { |c| c.name == col_name }
248
+ @errors << "Column '#{col_name}' does not exist in table '#{node.table_name}'"
249
+ end
250
+ end
251
+
252
+ # Check if index already exists
253
+ if table_info.indexes.any? { |idx| idx.name == node.name }
254
+ unless node.if_not_exists
255
+ @errors << "Index '#{node.name}' already exists"
256
+ end
257
+ end
258
+ end
259
+
260
+ def visit_drop_index(node)
261
+ # Check if index exists (we need to search all tables)
262
+ index_exists = false
263
+ @catalog.tables.each do |table|
264
+ if table.indexes.any? { |idx| idx.name == node.name }
265
+ index_exists = true
266
+ break
267
+ end
268
+ end
269
+
270
+ unless index_exists
271
+ unless node.if_exists
272
+ @errors << "Index '#{node.name}' does not exist"
273
+ end
274
+ end
275
+ end
276
+
277
+ def visit_create_database(node)
278
+ if @catalog.find_database(node.name)
279
+ unless node.if_not_exists
280
+ @errors << "Database '#{node.name}' already exists"
281
+ end
282
+ end
283
+ end
284
+
285
+ def visit_drop_database(node)
286
+ unless @catalog.find_database(node.name)
287
+ unless node.if_exists
288
+ @errors << "Database '#{node.name}' does not exist"
289
+ end
290
+ end
291
+ end
292
+
293
+ def visit_create_schema(node)
294
+ if @catalog.find_schema(node.name)
295
+ @errors << "Schema '#{node.name}' already exists" unless node.if_not_exists
296
+ end
297
+ end
298
+
299
+ def visit_drop_schema(node)
300
+ unless @catalog.find_schema(node.name)
301
+ @errors << "Schema '#{node.name}' does not exist" unless node.if_exists
302
+ end
303
+ end
304
+
305
+ def visit_create_view(node)
306
+ if @catalog.find_view(node.name)
307
+ @errors << "View '#{node.name}' already exists" unless node.if_not_exists
308
+ end
309
+ node.query.accept(self)
310
+ end
311
+
312
+ def visit_drop_view(node)
313
+ if !@catalog.find_view(node.name) && !node.if_exists
314
+ @errors << "View '#{node.name}' does not exist"
315
+ end
316
+ end
317
+
318
+ def visit_create_trigger(node)
319
+ unless @catalog.find_table(node.table_name)
320
+ @errors << "Table '#{node.table_name}' does not exist"
321
+ end
322
+ if @catalog.find_trigger(node.name)
323
+ @errors << "Trigger '#{node.name}' already exists"
324
+ end
325
+ end
326
+
327
+ def visit_drop_trigger(node)
328
+ if !@catalog.find_trigger(node.name) && !node.if_exists
329
+ @errors << "Trigger '#{node.name}' does not exist"
330
+ end
331
+ end
332
+
333
+ # --- Expression visitors ---
334
+
335
+ def visit_identifier(node)
336
+ # Check if identifier is in current scope
337
+ if @current_scope.key?(node.name)
338
+ # Resolve to column
339
+ node.table = @current_table_alias if node.table.nil?
340
+ column_info = @current_scope[node.name]
341
+ node.instance_variable_set(:@column_info, column_info)
342
+ else
343
+ @errors << "Column '#{node.name}' does not exist in current scope"
344
+ end
345
+ end
346
+
347
+ def visit_binary_op(node)
348
+ node.left.accept(self)
349
+ node.right.accept(self)
350
+
351
+ # Type compatibility check
352
+ left_type = get_expression_type(node.left)
353
+ right_type = get_expression_type(node.right)
354
+
355
+ if left_type && right_type && !type_compatible?(left_type, right_type, node.operator)
356
+ @warnings << "Type mismatch in binary operation: #{left_type} #{node.operator} #{right_type}"
357
+ end
358
+ end
359
+
360
+ def visit_unary_op(node)
361
+ node.operand.accept(self)
362
+ end
363
+
364
+ def visit_between(node)
365
+ node.expression.accept(self)
366
+ node.low.accept(self)
367
+ node.high.accept(self)
368
+ end
369
+
370
+ def visit_in(node)
371
+ node.expression.accept(self)
372
+ node.values.each(&:accept)
373
+ end
374
+
375
+ def visit_is_null(node)
376
+ node.expression.accept(self)
377
+ end
378
+
379
+ def visit_function_call(node)
380
+ # Validate function exists
381
+ unless function_exists?(node.name)
382
+ @errors << "Function '#{node.name}' does not exist"
383
+ end
384
+
385
+ node.arguments.each(&:accept)
386
+ end
387
+
388
+ def visit_parameter(node)
389
+ # Parameters are valid in any context
390
+ end
391
+
392
+ def visit_literal(node)
393
+ # Literals are always valid
394
+ end
395
+
396
+ def visit_star(node)
397
+ # Star is only valid in SELECT columns
398
+ unless @in_select_columns
399
+ @errors << "Invalid use of '*' outside SELECT column list"
400
+ end
401
+ end
402
+
403
+ # --- Constraint visitors ---
404
+
405
+ def visit_primary_key_constraint(node)
406
+ table_info = @current_table
407
+ if table_info
408
+ node.columns.each do |col_name|
409
+ unless table_info.columns.any? { |c| c.name == col_name }
410
+ @errors << "Column '#{col_name}' does not exist for PRIMARY KEY constraint"
411
+ end
412
+ end
413
+ end
414
+ end
415
+
416
+ def visit_foreign_key_constraint(node)
417
+ table_info = @current_table
418
+ if table_info
419
+ node.columns.each do |col_name|
420
+ unless table_info.columns.any? { |c| c.name == col_name }
421
+ @errors << "Column '#{col_name}' does not exist for FOREIGN KEY constraint"
422
+ end
423
+ end
424
+ end
425
+
426
+ # Validate referenced table exists
427
+ unless @catalog.find_table(node.reference_table)
428
+ @errors << "Referenced table '#{node.reference_table}' does not exist"
429
+ end
430
+ end
431
+
432
+ def visit_unique_constraint(node)
433
+ table_info = @current_table
434
+ if table_info
435
+ node.columns.each do |col_name|
436
+ unless table_info.columns.any? { |c| c.name == col_name }
437
+ @errors << "Column '#{col_name}' does not exist for UNIQUE constraint"
438
+ end
439
+ end
440
+ end
441
+ end
442
+
443
+ def visit_check_constraint(node)
444
+ node.condition.accept(self)
445
+ end
446
+
447
+ # --- Helper methods ---
448
+
449
+ private
450
+
451
+ def bind_table_reference(table_ref)
452
+ table_info = @catalog.find_table(table_ref.name)
453
+ unless table_info
454
+ @errors << "Table '#{table_ref.name}' does not exist"
455
+ return nil
456
+ end
457
+ table_info
458
+ end
459
+
460
+ def build_scope_from_table(table_info)
461
+ scope = {}
462
+ if table_info
463
+ table_info.columns.each do |col|
464
+ scope[col.name] = col
465
+ end
466
+ end
467
+ scope
468
+ end
469
+
470
+ def get_expression_type(expr)
471
+ return nil unless expr.respond_to?(:type)
472
+
473
+ if expr.type
474
+ expr.type
475
+ elsif expr.is_a?(AST::Identifier)
476
+ # Try to resolve from scope
477
+ @current_scope[expr.name]&.type
478
+ elsif expr.is_a?(AST::Literal)
479
+ map_literal_type(expr)
480
+ else
481
+ nil
482
+ end
483
+ end
484
+
485
+ def map_literal_type(literal)
486
+ case literal.token_type
487
+ when Token::Type::STRING
488
+ :text
489
+ when Token::Type::NUMBER
490
+ literal.value.is_a?(Integer) ? :integer : :float
491
+ when Token::Type::TRUE, Token::Type::FALSE
492
+ :boolean
493
+ when Token::Type::NULL
494
+ nil
495
+ else
496
+ nil
497
+ end
498
+ end
499
+
500
+ def type_compatible?(type1, type2, operator)
501
+ # Numeric types are compatible with each other
502
+ numeric_types = [:integer, :bigint, :smallint, :float, :decimal]
503
+ if numeric_types.include?(type1) && numeric_types.include?(type2)
504
+ return true
505
+ end
506
+
507
+ # Text types are compatible with each other
508
+ text_types = [:text, :varchar, :char]
509
+ if text_types.include?(type1) && text_types.include?(type2)
510
+ return true
511
+ end
512
+
513
+ # Same type is always compatible
514
+ return true if type1 == type2
515
+
516
+ # Comparison operators can handle most types
517
+ if [Token::Type::EQ, Token::Type::NE, Token::Type::LT,
518
+ Token::Type::LTE, Token::Type::GT, Token::Type::GTE].include?(operator)
519
+ return true
520
+ end
521
+
522
+ false
523
+ end
524
+
525
+ def valid_type?(type)
526
+ # Check if type is registered in type system
527
+ begin
528
+ Types::TypeRegistry.lookup(type)
529
+ true
530
+ rescue ConfigurationError
531
+ false
532
+ end
533
+ end
534
+
535
+ def function_exists?(name)
536
+ # Check if function is registered
537
+ # Built-in SQL functions are bound without a catalog entry.
538
+ common_functions = %w[
539
+ COUNT SUM AVG MAX MIN LOWER UPPER LENGTH
540
+ SUBSTR CONCAT COALESCE NULLIF
541
+ ROW_NUMBER RANK DENSE_RANK
542
+ DATE TIME TIMESTAMP EXTRACT
543
+ JSON_ARRAY JSON_OBJECT JSON_EXTRACT
544
+ ]
545
+ common_functions.include?(name.upcase)
546
+ end
547
+ end
548
+ end
549
+ end
550
+ end