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,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "timeout"
5
+ require "monitor"
6
+ require_relative "message"
7
+ require_relative "encoder"
8
+ require_relative "decoder"
9
+ require_relative "version"
10
+ require_relative "capabilities"
11
+ require_relative "handshake"
12
+
13
+ module RubyDB
14
+ module Protocol
15
+ class Protocol
16
+ attr_reader :encoder, :decoder, :handshake, :capabilities, :stats
17
+
18
+ def initialize(options = {})
19
+ @options = options
20
+ @encoder = Encoder.new(options[:format] || Encoder::FORMAT_JSON, options)
21
+ @decoder = Decoder.new(options)
22
+ @handshake = Handshake.new(options[:server_info])
23
+ @capabilities = Capabilities.default_server
24
+ @buffer = ""
25
+ @chunk_size = options[:chunk_size] || 4096
26
+ @socket = nil
27
+ @connected = false
28
+ @authenticated = false
29
+ @stats = {
30
+ messages_sent: 0,
31
+ messages_received: 0,
32
+ bytes_sent: 0,
33
+ bytes_received: 0,
34
+ errors: 0,
35
+ reconnects: 0,
36
+ connection_time: 0
37
+ }
38
+ @lock = Monitor.new
39
+ @read_timeout = options[:read_timeout] || 30
40
+ @write_timeout = options[:write_timeout] || 30
41
+ end
42
+
43
+ def connect(host, port)
44
+ @lock.synchronize do
45
+ start_time = Time.now
46
+
47
+ begin
48
+ @socket = TCPSocket.new(host, port)
49
+ @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
50
+
51
+ if @read_timeout
52
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, [@read_timeout, 0].pack("l_2"))
53
+ end
54
+
55
+ if @write_timeout
56
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, [@write_timeout, 0].pack("l_2"))
57
+ end
58
+
59
+ @connected = true
60
+ @stats[:connection_time] = (Time.now - start_time) * 1000
61
+
62
+ # Perform handshake
63
+ perform_handshake
64
+
65
+ true
66
+
67
+ rescue => e
68
+ @stats[:errors] += 1
69
+ @connected = false
70
+ raise ProtocolError, "Connection failed: #{e.message}"
71
+ end
72
+ end
73
+ end
74
+
75
+ def disconnect
76
+ @lock.synchronize do
77
+ if @socket
78
+ begin
79
+ send_message(Message.new(Message::TYPE_TERMINATE))
80
+ rescue
81
+ # Ignore errors on disconnect
82
+ end
83
+ @socket.close
84
+ end
85
+
86
+ @socket = nil
87
+ @connected = false
88
+ @authenticated = false
89
+ true
90
+ end
91
+ end
92
+
93
+ def send_message(message)
94
+ @lock.synchronize do
95
+ raise ProtocolError, "Not connected" unless @connected
96
+
97
+ begin
98
+ data = @encoder.encode(message)
99
+ @socket.write(data)
100
+ @socket.flush
101
+
102
+ @stats[:messages_sent] += 1
103
+ @stats[:bytes_sent] += data.bytesize
104
+
105
+ true
106
+ rescue => e
107
+ @stats[:errors] += 1
108
+ raise ProtocolError, "Send failed: #{e.message}"
109
+ end
110
+ end
111
+ end
112
+
113
+ def receive_message(timeout = @read_timeout)
114
+ @lock.synchronize do
115
+ raise ProtocolError, "Not connected" unless @connected
116
+
117
+ begin
118
+ Timeout.timeout(timeout) do
119
+ data = read_data
120
+ @stats[:bytes_received] += data.bytesize
121
+ @stats[:messages_received] += 1
122
+
123
+ @decoder.decode(data, @encoder.format)
124
+ end
125
+ rescue Timeout::Error
126
+ @stats[:errors] += 1
127
+ raise ProtocolError, "Receive timeout"
128
+ rescue => e
129
+ @stats[:errors] += 1
130
+ raise ProtocolError, "Receive failed: #{e.message}"
131
+ end
132
+ end
133
+ end
134
+
135
+ def query(sql, params = [])
136
+ message = Message.new(Message::TYPE_QUERY, { sql: sql, params: params })
137
+ send_message(message)
138
+ receive_message
139
+ end
140
+
141
+ def prepare(sql)
142
+ message = Message.new(Message::TYPE_PREPARE, { sql: sql })
143
+ send_message(message)
144
+ receive_message
145
+ end
146
+
147
+ def execute(statement_id, params = [])
148
+ message = Message.new(Message::TYPE_EXECUTE, { statement_id: statement_id, params: params })
149
+ send_message(message)
150
+ receive_message
151
+ end
152
+
153
+ def close_statement(statement_id)
154
+ message = Message.new(Message::TYPE_CLOSE, { statement_id: statement_id })
155
+ send_message(message)
156
+ receive_message
157
+ end
158
+
159
+ def begin_transaction
160
+ message = Message.new(Message::TYPE_BEGIN)
161
+ send_message(message)
162
+ receive_message
163
+ end
164
+
165
+ def commit
166
+ message = Message.new(Message::TYPE_COMMIT)
167
+ send_message(message)
168
+ receive_message
169
+ end
170
+
171
+ def rollback
172
+ message = Message.new(Message::TYPE_ROLLBACK)
173
+ send_message(message)
174
+ receive_message
175
+ end
176
+
177
+ def ping
178
+ message = Message.new(Message::TYPE_PING)
179
+ send_message(message)
180
+ receive_message
181
+ end
182
+
183
+ def connected?
184
+ @connected && @socket && !@socket.closed?
185
+ end
186
+
187
+ def authenticated?
188
+ @authenticated
189
+ end
190
+
191
+ def reconnect(host, port)
192
+ @lock.synchronize do
193
+ disconnect
194
+ connect(host, port)
195
+ @stats[:reconnects] += 1
196
+ true
197
+ end
198
+ end
199
+
200
+ def stats
201
+ @lock.synchronize do
202
+ @stats.merge({
203
+ connected: @connected,
204
+ authenticated: @authenticated,
205
+ handshake_state: @handshake.state,
206
+ buffer_size: @buffer.bytesize,
207
+ socket_info: @socket ? "#{@socket.peeraddr[2]}:#{@socket.peeraddr[1]}" : nil
208
+ })
209
+ end
210
+ end
211
+
212
+ private
213
+
214
+ def read_data
215
+ @socket.gets || ""
216
+ end
217
+
218
+ def perform_handshake
219
+ # Send handshake request
220
+ handshake_msg = Message.new(
221
+ Message::TYPE_HANDSHAKE,
222
+ {
223
+ protocol_version: ProtocolVersion.current,
224
+ client_name: "RubyDB Client",
225
+ client_version: RubyDB::VERSION
226
+ }
227
+ )
228
+
229
+ send_message(handshake_msg)
230
+ response = receive_message
231
+
232
+ if response.type == Message::TYPE_HANDSHAKE_RESPONSE
233
+ handshake_data = response.payload
234
+
235
+ if handshake_data[:success]
236
+ # Send authentication response
237
+ auth_msg = Message.new(
238
+ Message::TYPE_AUTHENTICATION_RESPONSE,
239
+ {
240
+ username: @options[:username] || "rubydb",
241
+ password: @options[:password] || "",
242
+ auth_method: @options[:auth_method] || "none"
243
+ }
244
+ )
245
+
246
+ send_message(auth_msg)
247
+ auth_response = receive_message
248
+
249
+ if auth_response.type == Message::TYPE_AUTHENTICATION
250
+ @authenticated = true
251
+ @handshake.state = Handshake::STATE_AUTHENTICATED
252
+
253
+ # Negotiate capabilities
254
+ caps_msg = Message.new(
255
+ Message::TYPE_SYNCHRONIZE,
256
+ {
257
+ capabilities: Capabilities.default_client.to_hash
258
+ }
259
+ )
260
+
261
+ send_message(caps_msg)
262
+ caps_response = receive_message
263
+
264
+ if caps_response.type == Message::TYPE_READY_FOR_QUERY
265
+ @handshake.state = Handshake::STATE_COMPLETE
266
+ @capabilities.negotiate(caps_response.payload[:capabilities] || 0)
267
+ @authenticated = true
268
+ end
269
+ end
270
+ else
271
+ raise ProtocolError, "Handshake failed: #{handshake_data[:error]}"
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Protocol
5
+ # ProtocolVersion - Defines protocol versions and compatibility
6
+ class ProtocolVersion
7
+ MAJOR = 1
8
+ MINOR = 0
9
+ PATCH = 0
10
+
11
+ VERSION_STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
12
+ VERSION_INT = (MAJOR << 16) | (MINOR << 8) | PATCH
13
+
14
+ # Supported protocol versions
15
+ SUPPORTED_VERSIONS = {
16
+ 0x010000 => "1.0.0",
17
+ 0x010001 => "1.0.1"
18
+ }
19
+
20
+ def self.current
21
+ VERSION_INT
22
+ end
23
+
24
+ def self.current_string
25
+ VERSION_STRING
26
+ end
27
+
28
+ def self.supported?(version)
29
+ SUPPORTED_VERSIONS.key?(version)
30
+ end
31
+
32
+ def self.parse(version_string)
33
+ parts = version_string.split(".").map(&:to_i)
34
+ return nil if parts.size != 3
35
+ (parts[0] << 16) | (parts[1] << 8) | parts[2]
36
+ end
37
+
38
+ def self.to_string(version_int)
39
+ major = (version_int >> 16) & 0xFF
40
+ minor = (version_int >> 8) & 0xFF
41
+ patch = version_int & 0xFF
42
+ "#{major}.#{minor}.#{patch}"
43
+ end
44
+
45
+ def self.compatible?(client_version, server_version)
46
+ # Major version must match
47
+ return false if (client_version >> 16) != (server_version >> 16)
48
+ # Minor version: server must support client's minor version
49
+ return false if (client_version & 0xFF00) > (server_version & 0xFF00)
50
+ true
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "connection"
4
+ require_relative "database_statements"
5
+ require_relative "schema_statements"
6
+ require_relative "quoting"
7
+ require_relative "type"
8
+ require_relative "transaction"
9
+ require_relative "migration"
10
+ require_relative "result"
11
+
12
+ module RubyDB
13
+ module Rails
14
+ # Adapter - Rails database adapter
15
+ class Adapter
16
+ attr_reader :config, :connection, :engine, :logger
17
+
18
+ include DatabaseStatements
19
+ include SchemaStatements
20
+ include Quoting
21
+
22
+ ADAPTER_NAME = "RubyDB"
23
+
24
+ def initialize(config)
25
+ @config = config
26
+ @engine = config[:engine]
27
+ @logger = config[:logger]
28
+ @connection = Connection.new(config)
29
+ @connection.connect
30
+ @pool = nil
31
+ @statements = {}
32
+ @statement_counter = 0
33
+ @prepared_statements = {}
34
+ @transaction = nil
35
+ @lock = Mutex.new
36
+ end
37
+
38
+ def self.new(config)
39
+ adapter = allocate
40
+ adapter.send(:initialize, config)
41
+ adapter
42
+ end
43
+
44
+ def adapter_name
45
+ ADAPTER_NAME
46
+ end
47
+
48
+ def supports_migrations?
49
+ true
50
+ end
51
+
52
+ def supports_primary_key?
53
+ true
54
+ end
55
+
56
+ def supports_index_sort_order?
57
+ true
58
+ end
59
+
60
+ def supports_transactions?
61
+ true
62
+ end
63
+
64
+ def supports_savepoints?
65
+ true
66
+ end
67
+
68
+ def supports_foreign_keys?
69
+ true
70
+ end
71
+
72
+ def supports_views?
73
+ true
74
+ end
75
+
76
+ def supports_json?
77
+ true
78
+ end
79
+
80
+ def supports_uuid?
81
+ true
82
+ end
83
+
84
+ def primary_key(table_name)
85
+ column = columns(table_name).find { |entry| entry[:primary_key] }
86
+ column ? column[:name] : "id"
87
+ end
88
+
89
+ def tables
90
+ metadata_engine.list_tables.map(&:to_s)
91
+ end
92
+
93
+ def table_exists?(table_name)
94
+ tables.include?(table_name)
95
+ end
96
+
97
+ def indexes(table_name)
98
+ manager = metadata_engine.index_manager
99
+ manager.get_indexes_for_table(table_name).map do |index|
100
+ {
101
+ name: index.name.to_s,
102
+ columns: index.columns.map(&:to_s),
103
+ unique: index.unique
104
+ }
105
+ end
106
+ end
107
+
108
+ def columns(table_name)
109
+ metadata_engine.table_columns(table_name).map do |column|
110
+ {
111
+ name: column.name.to_s,
112
+ type: column.type,
113
+ default: normalize_catalog_default(column.default),
114
+ null: column.nullable?,
115
+ primary_key: column.primary_key?
116
+ }
117
+ end
118
+ end
119
+
120
+ def column_exists?(table_name, column_name)
121
+ columns(table_name).any? { |c| c[:name] == column_name }
122
+ end
123
+
124
+ def schema_version
125
+ return nil unless table_exists?("schema_migrations")
126
+
127
+ result = execute("SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1")
128
+ result.first && (result.first["version"] || result.first[:version])
129
+ end
130
+
131
+ def dump_schema
132
+ schema = +""
133
+
134
+ tables.each do |table|
135
+ table_columns = columns(table)
136
+ primary_key = table_columns.find { |col| col[:primary_key] }
137
+ automatic_id = @engine && primary_key && primary_key[:name].to_s == "id" && primary_key[:type].to_sym == :integer
138
+ table_options = automatic_id ? "" : ", id: false"
139
+ schema << "create_table \"#{table}\"#{table_options} do |t|\n"
140
+ table_columns.each do |col|
141
+ next if automatic_id && col[:primary_key]
142
+
143
+ type = Type.to_rails(col[:type])
144
+ schema << " t.#{type} \"#{col[:name]}\""
145
+ schema << ", primary_key: true" if col[:primary_key]
146
+ schema << ", default: #{schema_literal(col[:default])}" if col.key?(:default) && !col[:default].nil?
147
+ schema << ", null: false" unless col[:null]
148
+ schema << "\n"
149
+ end
150
+ schema << "end\n\n"
151
+
152
+ # Schema dumps can be generated from an adapter double or a
153
+ # connection-only configuration where catalog introspection is not
154
+ # available. In a live embedded adapter, always use the real index
155
+ # catalog; otherwise preserve the column-only dump contract.
156
+ table_indexes = @engine ? indexes(table) : []
157
+ table_indexes.each do |index|
158
+ schema << "add_index \"#{table}\", #{index[:columns].map(&:to_s).inspect}"
159
+ schema << ", unique: true" if index[:unique]
160
+ schema << ", name: #{index[:name].to_s.inspect}"
161
+ schema << "\n"
162
+ end
163
+ schema << "\n" if table_indexes.any?
164
+ end
165
+
166
+ schema
167
+ end
168
+
169
+ def reconnect!
170
+ @connection.disconnect
171
+ @connection.connect
172
+ end
173
+
174
+ def disconnect!
175
+ @connection.disconnect
176
+ end
177
+
178
+ def reset!
179
+ reconnect!
180
+ @statements.clear
181
+ @prepared_statements.clear
182
+ end
183
+
184
+ def close
185
+ @connection.disconnect
186
+ end
187
+
188
+ def active?
189
+ @connection.connected?
190
+ end
191
+
192
+ def stats
193
+ @connection.stats
194
+ end
195
+
196
+ private
197
+
198
+ def metadata_engine
199
+ return @engine if @engine
200
+
201
+ raise ConnectionError, "RubyDB catalog introspection requires an embedded :engine"
202
+ end
203
+
204
+ def normalize_catalog_default(value)
205
+ value.respond_to?(:value) ? value.value : value
206
+ end
207
+
208
+ def parse_index_columns(sql)
209
+ if sql =~ /\(([^)]+)\)/
210
+ $1.split(",").map(&:strip)
211
+ else
212
+ []
213
+ end
214
+ end
215
+
216
+ def schema_literal(value)
217
+ case value
218
+ when true then @engine ? "true" : (respond_to?(:quote) ? quote(value) : "true")
219
+ when false then @engine ? "false" : (respond_to?(:quote) ? quote(value) : "false")
220
+ when nil then "nil"
221
+ when Numeric then value.to_s
222
+ else value.to_s.inspect
223
+ end
224
+ end
225
+
226
+ def log(sql, name = nil, &block)
227
+ start_time = Time.now
228
+ result = block.call
229
+ elapsed_ms = (Time.now - start_time) * 1000
230
+
231
+ if @logger
232
+ @logger.debug " #{name || 'SQL'} (#{elapsed_ms.round(2)}ms) #{sql}"
233
+ end
234
+
235
+ result
236
+ end
237
+ end
238
+ end
239
+ end