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,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module CLI
5
+ module Commands
6
+ class Restore
7
+ def initialize(output, formatter)
8
+ @output = output
9
+ @formatter = formatter
10
+ end
11
+
12
+ def execute(args, options)
13
+ parser = OptionParser.new do |opts|
14
+ opts.banner = "Usage: rubydb restore [options]"
15
+ opts.on("-b", "--backup NAME", "Backup name") do |name|
16
+ options[:backup] = name
17
+ end
18
+ opts.on("-l", "--latest", "Restore latest backup") do
19
+ options[:latest] = true
20
+ end
21
+ opts.on("--point-in-time TIME", "Restore to point in time") do |time|
22
+ options[:point_in_time] = time
23
+ end
24
+ opts.on("--dry-run", "Show what would be restored") do
25
+ options[:dry_run] = true
26
+ end
27
+ opts.on("-f", "--force", "Force restore") do
28
+ options[:force] = true
29
+ end
30
+ opts.on("--database PATH", "Database path") { |path| options[:database] = path }
31
+ opts.on("--dir DIR", "Backup directory") { |path| options[:backup_dir] = path }
32
+ opts.on("-h", "--help", "Show help") do
33
+ @output.puts opts
34
+ exit(0)
35
+ end
36
+ end
37
+
38
+ parser.parse!(args)
39
+
40
+ # Get real config
41
+ config = RubyDB::Configuration::Config.instance
42
+ db_path = options[:database] || config.get("storage.data_dir") || "data/rubydb.rdb"
43
+ backup_dir = options[:backup_dir] || config.get("backup.backup_dir") || "backups"
44
+
45
+ selected_path = if options[:backup]
46
+ File.join(backup_dir, options[:backup])
47
+ elsif options[:latest]
48
+ nil
49
+ end
50
+
51
+ if options[:dry_run]
52
+ available = RubyDB::Backup::Restore.new(nil, backup_dir: backup_dir).list_available_backups
53
+ selected = if selected_path
54
+ available.find { |backup| backup[:path] == selected_path }
55
+ else
56
+ available.first
57
+ end
58
+ raise "No matching backup available" unless selected
59
+ @output.info("Would restore #{selected[:name]} from #{selected[:path]}")
60
+ return 0
61
+ end
62
+
63
+ unless options[:force]
64
+ @output.warn("This will overwrite the current database")
65
+ @output.print("Are you sure? (yes/no): ", nil, false)
66
+ response = $stdin.gets.chomp
67
+ return 1 unless response.downcase == "yes"
68
+ end
69
+
70
+ # Create restore instance
71
+ engine = RubyDB::Storage::Engine.new(db_path, {})
72
+ restore = RubyDB::Backup::Restore.new(engine, backup_dir: backup_dir)
73
+
74
+ if options[:latest]
75
+ @output.spinner("Restoring latest backup...") do
76
+ result = restore.restore_latest
77
+
78
+ if result[:success]
79
+ @output.success("Backup restored successfully")
80
+ else
81
+ @output.error("Restore failed: #{result[:error]}")
82
+ return 1
83
+ end
84
+ end
85
+ elsif options[:backup]
86
+ backup_path = File.join(backup_dir, options[:backup])
87
+ @output.spinner("Restoring backup #{options[:backup]}...") do
88
+ result = restore.restore(backup_path)
89
+
90
+ if result[:success]
91
+ @output.success("Backup restored successfully")
92
+ else
93
+ @output.error("Restore failed: #{result[:error]}")
94
+ return 1
95
+ end
96
+ end
97
+ elsif options[:point_in_time]
98
+ @output.spinner("Restoring to point in time #{options[:point_in_time]}...") do
99
+ result = restore.restore_point_in_time(options[:point_in_time])
100
+
101
+ if result[:success]
102
+ @output.success("Database restored to #{options[:point_in_time]}")
103
+ else
104
+ @output.error("Restore failed: #{result[:error]}")
105
+ return 1
106
+ end
107
+ end
108
+ else
109
+ @output.error("Please specify --backup, --latest, or --point-in-time")
110
+ @output.puts parser
111
+ return 1
112
+ end
113
+
114
+ 0
115
+ ensure
116
+ engine&.close if engine&.open?
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,365 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "readline"
5
+ rescue LoadError
6
+ # The interactive shell is optional for library and adapter consumers.
7
+ # `execute` reports a focused error if the command is requested without it.
8
+ end
9
+ require "json"
10
+
11
+ module RubyDB
12
+ module CLI
13
+ module Commands
14
+ class Shell
15
+ def initialize(output, formatter)
16
+ @output = output
17
+ @formatter = formatter
18
+ @history_file = File.expand_path("~/.rubydb_history")
19
+ @connected = false
20
+ @engine = nil
21
+ @current_database = nil
22
+ @transaction = nil
23
+ end
24
+
25
+ def execute(args, options)
26
+ unless defined?(Readline)
27
+ raise RubyDB::Error, "The interactive shell requires the readline extension"
28
+ end
29
+
30
+ parser = OptionParser.new do |opts|
31
+ opts.banner = "Usage: rubydb shell [options]"
32
+ opts.on("-d", "--database NAME", "Database name") do |name|
33
+ options[:database] = name
34
+ end
35
+ opts.on("-h", "--host HOST", "Server host") do |host|
36
+ options[:host] = host
37
+ end
38
+ opts.on("-p", "--port PORT", Integer, "Server port") do |port|
39
+ options[:port] = port
40
+ end
41
+ opts.on("-u", "--user USER", "Username") do |user|
42
+ options[:user] = user
43
+ end
44
+ opts.on("-w", "--password", "Prompt for password") do
45
+ options[:password] = true
46
+ end
47
+ opts.on("--csv", "Output in CSV format") do
48
+ options[:csv] = true
49
+ end
50
+ opts.on("--json", "Output in JSON format") do
51
+ options[:json] = true
52
+ end
53
+ opts.on("--table", "Output in table format") do
54
+ options[:table] = true
55
+ end
56
+ opts.on("-h", "--help", "Show help") do
57
+ @output.puts opts
58
+ exit(0)
59
+ end
60
+ end
61
+
62
+ parser.parse!(args)
63
+ @options = options
64
+
65
+ # Load history
66
+ load_history
67
+
68
+ # Connect to database
69
+ connect_to_database(options)
70
+
71
+ @output.puts "RubyDB Shell v#{RubyDB::VERSION}", :bold
72
+ @output.puts "Connected to: #{@current_database || 'rubydb'}"
73
+ @output.puts "Type '.help' for help, '.exit' to quit"
74
+ @output.puts
75
+
76
+ @connected = true
77
+
78
+ while @connected
79
+ begin
80
+ prompt = @current_database ? "#{@current_database}> " : "rubydb> "
81
+ input = Readline.readline(prompt, true)
82
+ break if input.nil?
83
+
84
+ input = input.strip
85
+ next if input.empty?
86
+
87
+ # Handle special commands
88
+ case input
89
+ when ".exit", ".quit"
90
+ break
91
+ when ".help", ".?"
92
+ print_help
93
+ when ".status"
94
+ print_status
95
+ when ".clear", ".cls"
96
+ system("clear") || system("cls")
97
+ when /\A\.db\s+(.+)/
98
+ switch_database($1)
99
+ when /\A\.(?:table|t)\s+(.+)/
100
+ describe_table($1)
101
+ when /\A\.tables/
102
+ list_tables
103
+ when /\A\.(?:schema|s)\s+(.+)/
104
+ show_schema($1)
105
+ when /\A\.(?:begin|txn|start)/
106
+ begin_transaction
107
+ when /\A\.commit/
108
+ commit_transaction
109
+ when /\A\.rollback/
110
+ rollback_transaction
111
+ when /\A\.explain\s+(.+)/
112
+ explain_query($1)
113
+ else
114
+ execute_query(input)
115
+ end
116
+ rescue Interrupt
117
+ @output.puts
118
+ @output.info("Type .exit to quit")
119
+ rescue => e
120
+ @output.error(e.message)
121
+ @output.debug(e.backtrace.join("\n")) if ENV["RUBYDB_DEBUG"]
122
+ end
123
+ end
124
+
125
+ # Cleanup
126
+ disconnect
127
+ save_history
128
+ @output.puts "Goodbye!"
129
+
130
+ 0
131
+ end
132
+
133
+ private
134
+
135
+ def connect_to_database(options)
136
+ config = RubyDB::Configuration::Config.instance
137
+
138
+ db_name = options[:database] || config.get("database.name") || "rubydb"
139
+ db_path = config.get("storage.data_dir") || "data"
140
+ db_file = File.join(db_path, "#{db_name}.rdb")
141
+
142
+ @engine = RubyDB::Storage::Engine.new(db_file, {})
143
+ @current_database = db_name
144
+ @connected = true
145
+ rescue => e
146
+ @output.error("Failed to connect: #{e.message}")
147
+ exit(1)
148
+ end
149
+
150
+ def disconnect
151
+ @engine.close if @engine
152
+ @connected = false
153
+ end
154
+
155
+ def execute_query(sql)
156
+ @output.spinner("Executing query...") do
157
+ # Parse SQL
158
+ parser = RubyDB::SQL::Parser.new(RubyDB::SQL::Lexer.new(sql).tokenize)
159
+ statements = parser.parse
160
+
161
+ statements.each do |stmt|
162
+ result = execute_statement(stmt)
163
+ display_result(result)
164
+ end
165
+ end
166
+ rescue => e
167
+ @output.error(e.message)
168
+ end
169
+
170
+ def execute_statement(statement)
171
+ # Plan and execute the statement
172
+ planner = RubyDB::Execution::Planner.new(@engine)
173
+ plan = planner.plan(statement)
174
+
175
+ executor = RubyDB::Execution::Executor.new(@engine)
176
+ executor.execute(plan)
177
+ end
178
+
179
+ def display_result(result)
180
+ rows = result[:rows] || []
181
+ columns = result[:column_names] || []
182
+
183
+ if rows.empty?
184
+ @output.puts("0 rows returned", :gray)
185
+ return
186
+ end
187
+
188
+ if @options[:json]
189
+ @output.json(rows)
190
+ elsif @options[:csv]
191
+ # Output CSV
192
+ @output.puts columns.join(",")
193
+ rows.each { |row| @output.puts columns.map { |c| row[c].to_s }.join(",") }
194
+ elsif @options[:table] != false
195
+ # Table format
196
+ table_rows = rows.map { |row| columns.map { |c| row[c].to_s } }
197
+ @output.table(columns, table_rows, show_count: true)
198
+ else
199
+ # Default format
200
+ rows.each do |row|
201
+ @output.puts row.inspect
202
+ end
203
+ @output.puts "(#{rows.size} rows)", :gray
204
+ end
205
+ end
206
+
207
+ def switch_database(name)
208
+ db_path = File.join("data", "#{name}.rdb")
209
+ if File.exist?(db_path)
210
+ @engine.close if @engine
211
+ @engine = RubyDB::Storage::Engine.new(db_path, {})
212
+ @current_database = name
213
+ @output.success("Switched to database: #{name}")
214
+ else
215
+ @output.error("Database '#{name}' does not exist")
216
+ end
217
+ end
218
+
219
+ def list_tables
220
+ tables = @engine.list_tables
221
+ if tables.empty?
222
+ @output.puts("No tables found", :gray)
223
+ else
224
+ @output.puts("Tables in #{@current_database}:", :bold)
225
+ tables.each { |t| @output.puts(" - #{t}") }
226
+ @output.puts("Total: #{tables.size}", :gray)
227
+ end
228
+ end
229
+
230
+ def describe_table(name)
231
+ columns = @engine.table_columns(name)
232
+ if columns.empty?
233
+ @output.error("Table '#{name}' not found")
234
+ return
235
+ end
236
+
237
+ @output.puts("Table: #{name}", :bold)
238
+ @output.puts("-" * 40)
239
+ columns.each do |col|
240
+ pk = col.primary_key? ? " PK" : ""
241
+ null = col.nullable? ? "" : " NOT NULL"
242
+ default = col.has_default? ? " DEFAULT #{col.default}" : ""
243
+ @output.puts(" #{col.name}: #{col.type}#{pk}#{null}#{default}")
244
+ end
245
+ @output.puts("-" * 40)
246
+ @output.puts("Total columns: #{columns.size}", :gray)
247
+ end
248
+
249
+ def show_schema(name)
250
+ columns = @engine.table_columns(name)
251
+ if columns.empty?
252
+ @output.error("Table '#{name}' not found")
253
+ return
254
+ end
255
+
256
+ sql = "CREATE TABLE #{name} (\n"
257
+ cols = columns.map do |col|
258
+ " #{col.name} #{col.type}#{col.primary_key? ? " PRIMARY KEY" : ""}"
259
+ end
260
+ sql << cols.join(",\n")
261
+ sql << "\n);"
262
+ @output.puts(sql, :cyan)
263
+ end
264
+
265
+ def begin_transaction
266
+ @transaction = @engine.begin_transaction
267
+ @output.success("Transaction started")
268
+ end
269
+
270
+ def commit_transaction
271
+ if @transaction
272
+ @engine.commit_transaction(@transaction)
273
+ @transaction = nil
274
+ @output.success("Transaction committed")
275
+ else
276
+ @output.error("No active transaction")
277
+ end
278
+ end
279
+
280
+ def rollback_transaction
281
+ if @transaction
282
+ @engine.rollback_transaction(@transaction)
283
+ @transaction = nil
284
+ @output.success("Transaction rolled back")
285
+ else
286
+ @output.error("No active transaction")
287
+ end
288
+ end
289
+
290
+ def explain_query(sql)
291
+ parser = RubyDB::SQL::Parser.new(RubyDB::SQL::Lexer.new(sql).tokenize)
292
+ stmt = parser.parse.first
293
+ planner = RubyDB::Execution::Planner.new(@engine)
294
+ plan = planner.plan(stmt)
295
+
296
+ @output.puts("EXPLAIN QUERY PLAN", :bold)
297
+ @output.puts("-" * 40)
298
+ @output.puts(" Scan Type: #{plan.scan_type}")
299
+ @output.puts(" Table: #{plan.table_name}")
300
+ @output.puts(" Estimated Cost: #{plan.estimated_cost}")
301
+ @output.puts(" Estimated Rows: #{plan.estimated_rows}")
302
+ @output.puts(" Predicate: #{plan.predicate.inspect}") if plan.predicate
303
+ @output.puts(" Order By: #{plan.order_by.inspect}") if plan.order_by&.any?
304
+ @output.puts(" Limit: #{plan.limit}") if plan.limit
305
+ @output.puts("-" * 40)
306
+ end
307
+
308
+ def print_help
309
+ @output.puts("\nAvailable Commands:", :bold)
310
+ @output.puts(" .help, .? Show this help")
311
+ @output.puts(" .exit, .quit Exit the shell")
312
+ @output.puts(" .status Show connection status")
313
+ @output.puts(" .clear, .cls Clear screen")
314
+ @output.puts(" .db <name> Switch database")
315
+ @output.puts(" .tables List all tables")
316
+ @output.puts(" .table <name> Describe a table")
317
+ @output.puts(" .schema <name> Show CREATE TABLE statement")
318
+ @output.puts(" .begin, .txn Begin transaction")
319
+ @output.puts(" .commit Commit transaction")
320
+ @output.puts(" .rollback Rollback transaction")
321
+ @output.puts(" .explain <sql> Explain query plan")
322
+ @output.puts("\nAny other input is treated as SQL", :gray)
323
+ end
324
+
325
+ def print_status
326
+ @output.puts("\nConnection Status:", :bold)
327
+ @output.puts(" Connected: #{@connected}")
328
+ @output.puts(" Database: #{@current_database}")
329
+ @output.puts(" Version: #{RubyDB::VERSION}")
330
+ @output.puts(" PID: #{Process.pid}")
331
+ @output.puts(" In Transaction: #{!@transaction.nil?}")
332
+ @output.puts(" Tables: #{@engine.list_tables.size}")
333
+ @output.puts(" Storage: #{format_size(get_db_size)}")
334
+ end
335
+
336
+ def get_db_size
337
+ db_path = File.join("data", "#{@current_database}.rdb")
338
+ File.exist?(db_path) ? File.size(db_path) : 0
339
+ end
340
+
341
+ def format_size(bytes)
342
+ return "0 B" if bytes == 0
343
+ units = ["B", "KB", "MB", "GB", "TB"]
344
+ exp = (Math.log(bytes) / Math.log(1024)).floor
345
+ size = bytes / (1024.0 ** exp)
346
+ "#{size.round(2)} #{units[exp]}"
347
+ end
348
+
349
+ def load_history
350
+ return unless File.exist?(@history_file)
351
+ File.readlines(@history_file).each { |line| Readline::HISTORY.push(line.chomp) }
352
+ rescue
353
+ # Ignore
354
+ end
355
+
356
+ def save_history
357
+ history = Readline::HISTORY.to_a
358
+ File.write(@history_file, history.join("\n"))
359
+ rescue
360
+ # Ignore
361
+ end
362
+ end
363
+ end
364
+ end
365
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module CLI
5
+ module Commands
6
+ # Snapshot - Create a database snapshot
7
+ class Snapshot
8
+ def initialize(output, formatter)
9
+ @output = output
10
+ @formatter = formatter
11
+ end
12
+
13
+ def execute(args, options)
14
+ parser = OptionParser.new do |opts|
15
+ opts.banner = "Usage: rubydb snapshot [options]"
16
+ opts.on("-n", "--name NAME", "Snapshot name") do |name|
17
+ options[:name] = name
18
+ end
19
+ opts.on("-l", "--list", "List snapshots") do
20
+ options[:list] = true
21
+ end
22
+ opts.on("-d", "--delete NAME", "Delete snapshot") do |name|
23
+ options[:delete] = name
24
+ end
25
+ opts.on("-r", "--restore NAME", "Restore snapshot") do |name|
26
+ options[:restore] = name
27
+ end
28
+ opts.on("--database PATH", "Database path") { |path| options[:database] = path }
29
+ opts.on("--dir DIR", "Snapshot directory") { |path| options[:snapshot_dir] = path }
30
+ opts.on("-h", "--help", "Show help") do
31
+ @output.puts opts
32
+ exit(0)
33
+ end
34
+ end
35
+
36
+ parser.parse!(args)
37
+
38
+ database = RubyDB::Database.new(options[:database] || "data/rubydb.rdb", auto_connect: false).connect
39
+ snapshots = RubyDB::Backup::Snapshot.new(
40
+ database.engine, snapshot_dir: options[:snapshot_dir] || "snapshots"
41
+ )
42
+
43
+ if options[:list]
44
+ @formatter.format_snapshots(snapshots.list_snapshots)
45
+ return 0
46
+ end
47
+
48
+ if options[:delete]
49
+ result = snapshots.delete_snapshot(options[:delete])
50
+ raise result[:error] unless result[:success]
51
+ @output.success("Snapshot #{options[:delete]} deleted")
52
+ return 0
53
+ end
54
+
55
+ if options[:restore]
56
+ @output.warn("This will overwrite the current database")
57
+ @output.print("Are you sure? (yes/no): ", nil, false)
58
+ response = $stdin.gets.chomp
59
+ return unless response.downcase == "yes"
60
+
61
+ result = snapshots.restore_snapshot(options[:restore])
62
+ raise result[:error] unless result[:success]
63
+ @output.success("Snapshot #{options[:restore]} restored")
64
+ return 0
65
+ end
66
+
67
+ name = options[:name] || "snapshot_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
68
+
69
+ result = snapshots.create_snapshot(name)
70
+ raise result[:error] unless result[:success]
71
+ @output.success("Snapshot #{name} created")
72
+ 0
73
+ ensure
74
+ database&.close
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module RubyDB
6
+ module CLI
7
+ module Commands
8
+ # Start - Start the database server
9
+ class Start
10
+ def initialize(output, formatter)
11
+ @output = output
12
+ @formatter = formatter
13
+ end
14
+
15
+ def execute(args, options)
16
+ parser = OptionParser.new do |opts|
17
+ opts.banner = "Usage: rubydb start [options]"
18
+ opts.on("-d", "--daemon", "Run as daemon") do
19
+ options[:daemon] = true
20
+ end
21
+ opts.on("-p", "--port PORT", Integer, "Server port") do |port|
22
+ options[:port] = port
23
+ end
24
+ opts.on("--data-dir DIR", "Database data directory") { |dir| options[:data_dir] = dir }
25
+ opts.on("--log-dir DIR", "Server log directory") { |dir| options[:log_dir] = dir }
26
+ opts.on("-H", "--host HOST", "Server host") do |host|
27
+ options[:host] = host
28
+ end
29
+ opts.on("--pid-file FILE", "PID file") do |file|
30
+ options[:pid_file] = file
31
+ end
32
+ opts.on("-h", "--help", "Show help") do
33
+ @output.puts opts
34
+ exit(0)
35
+ end
36
+ end
37
+
38
+ parser.parse!(args)
39
+
40
+ configured = load_runtime_config(options)
41
+ server_config = configured[:server] || {}
42
+
43
+ server_options = {
44
+ host: options[:host] || server_config[:host] || "localhost",
45
+ port: options[:port] || server_config[:port] || 7432,
46
+ data_dir: options[:data_dir] || configured.dig(:storage, :data_dir) || "data",
47
+ log_dir: options[:log_dir] || configured.dig(:logging, :log_dir) || "log",
48
+ pid_file: options[:pid_file] || server_config[:pid_file] || "rubydb.pid",
49
+ daemonize: options[:daemon] == true || server_config[:daemonize] == true,
50
+ max_connections: server_config[:max_connections],
51
+ min_workers: server_config[:min_workers],
52
+ max_workers: server_config[:max_workers],
53
+ read_timeout: server_config[:read_timeout],
54
+ write_timeout: server_config[:write_timeout],
55
+ idle_timeout: server_config[:idle_timeout],
56
+ max_request_size: server_config[:max_request_size],
57
+ authentication: (options[:config] || options[:env].to_s == "production") ? configured[:auth] : { method: "none" },
58
+ ssl: configured[:ssl] || { enabled: false }
59
+ }.compact
60
+ server = RubyDB::Server::Server.new(server_options)
61
+ server.start
62
+ @output.success("RubyDB server started on #{server.config[:host]}:#{server.config[:port]}")
63
+ return 0 if options[:daemon]
64
+
65
+ trap("INT") { server.stop; exit(0) }
66
+ trap("TERM") { server.stop; exit(0) }
67
+ sleep
68
+ end
69
+
70
+ private
71
+
72
+ def load_runtime_config(options)
73
+ RubyDB::Configuration::Config.environment = options[:env] if options[:env]
74
+ config = RubyDB::Configuration::Config.load(options[:config])
75
+ raise "Invalid configuration: #{RubyDB::Configuration::Config.errors.join('; ')}" unless config && RubyDB::Configuration::Config.valid?
76
+
77
+ config
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end