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,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "quoting"
4
+ require_relative "result"
5
+ require_relative "type"
6
+
7
+ module RubyDB
8
+ module Rails
9
+ # Connection - Rails database connection
10
+ class Connection
11
+ attr_reader :config, :client, :engine, :logger, :transaction
12
+
13
+ include Quoting
14
+
15
+ def initialize(config)
16
+ @config = config
17
+ @engine = config[:engine]
18
+ @engine_owned = false
19
+ if @engine.nil? && embedded_config?
20
+ @engine = build_embedded_engine
21
+ @engine_owned = true
22
+ end
23
+ @logger = config[:logger]
24
+ @client = nil
25
+ @transaction = nil
26
+ @transaction_depth = 0
27
+ @statements = {}
28
+ @statement_counter = 0
29
+ @connected = false
30
+ @lock = Mutex.new
31
+ @query_cache = {}
32
+ @query_cache_enabled = false
33
+ @query_cache_size = 100
34
+ @last_query_time = nil
35
+ end
36
+
37
+ def connect
38
+ @lock.synchronize do
39
+ return if @connected
40
+
41
+ if @engine.nil? && embedded_config?
42
+ @engine = build_embedded_engine
43
+ @engine_owned = true
44
+ end
45
+
46
+ if @engine
47
+ if @engine.respond_to?(:open?) && !@engine.open?
48
+ raise ConnectionError, "Embedded RubyDB engine is not open"
49
+ end
50
+ @connected = true
51
+ return
52
+ end
53
+
54
+ url = @config[:url] || @config["url"]
55
+ client_config = if url
56
+ {url: url}
57
+ else
58
+ {
59
+ host: @config[:host] || "localhost",
60
+ port: @config[:port] || 7432,
61
+ username: @config[:username] || "rubydb",
62
+ password: @config[:password] || "",
63
+ database: @config[:database] || "rubydb"
64
+ }
65
+ end
66
+ client_config[:timeout] = @config[:timeout] || @config["timeout"] || 30
67
+ client_config[:pool_size] = @config[:pool_size] || 1
68
+ client_config[:ssl] = @config[:ssl] || @config["ssl"] if @config[:ssl] || @config["ssl"]
69
+ client_config[:compress] = @config[:compress] || @config["compress"] if @config[:compress] || @config["compress"]
70
+ client_config[:format] = @config[:format] || @config["format"] if @config[:format] || @config["format"]
71
+ @client = RubyDB::Client::Client.new(client_config)
72
+ @client.connect
73
+ @connected = true
74
+ end
75
+ end
76
+
77
+ def disconnect
78
+ @lock.synchronize do
79
+ return unless @connected
80
+
81
+ @client&.disconnect unless @engine
82
+ if @engine_owned && @engine.respond_to?(:open?) && @engine.open?
83
+ @engine.close
84
+ @engine = nil
85
+ end
86
+ @connected = false
87
+ @statements.clear
88
+ end
89
+ end
90
+
91
+ def connected?
92
+ return @connected && (!@engine.respond_to?(:open?) || @engine.open?) if @engine
93
+
94
+ @connected && @client&.connected?
95
+ end
96
+
97
+ def execute(sql, params = [])
98
+ ensure_connected
99
+
100
+ if @query_cache_enabled && sql =~ /^SELECT/i
101
+ cache_key = "#{sql}:#{params.join(':')}"
102
+ if @query_cache.key?(cache_key)
103
+ return @query_cache[cache_key]
104
+ end
105
+ end
106
+
107
+ rails_result = if @engine
108
+ Result.new(execute_embedded(sql, params))
109
+ else
110
+ result = @client.query(sql, params)
111
+ Result.new(result.to_hash)
112
+ end
113
+
114
+ if @query_cache_enabled && sql =~ /^SELECT/i && @query_cache.size < @query_cache_size
115
+ @query_cache[cache_key] = rails_result
116
+ end
117
+
118
+ rails_result
119
+ end
120
+
121
+ def prepare(sql)
122
+ ensure_connected
123
+ if @engine
124
+ @statement_counter += 1
125
+ @statements[@statement_counter] = sql
126
+ return @statement_counter
127
+ end
128
+ @client.prepare(sql)
129
+ end
130
+
131
+ def execute_prepared(statement_id, params = [])
132
+ ensure_connected
133
+ return execute(@statements.fetch(statement_id), params) if @engine
134
+
135
+ result = @client.execute(statement_id, params)
136
+ Result.new(result.to_hash)
137
+ end
138
+
139
+ def close_statement(statement_id)
140
+ ensure_connected
141
+ return @statements.delete(statement_id) if @engine
142
+
143
+ @client.close_statement(statement_id)
144
+ end
145
+
146
+ def begin_db_transaction
147
+ ensure_connected
148
+ @transaction_depth += 1
149
+ if @engine && @transaction_depth == 1
150
+ @engine.begin_transaction
151
+ @transaction = @engine.current_transaction
152
+ end
153
+ @client.begin_transaction if !@engine && @transaction_depth == 1
154
+ end
155
+
156
+ def commit_db_transaction
157
+ ensure_connected
158
+ return if @transaction_depth <= 0
159
+
160
+ @transaction_depth -= 1
161
+ if @transaction_depth == 0
162
+ @engine.commit_transaction(@transaction) if @engine
163
+ @client.commit unless @engine
164
+ @transaction = nil
165
+ end
166
+ end
167
+
168
+ def rollback_db_transaction
169
+ ensure_connected
170
+ return if @transaction_depth <= 0
171
+
172
+ @transaction_depth -= 1
173
+ if @transaction_depth == 0
174
+ @engine.rollback_transaction(@transaction) if @engine
175
+ @client.rollback unless @engine
176
+ @transaction = nil
177
+ end
178
+ @transaction_depth = 0 if @transaction_depth < 0
179
+ end
180
+
181
+ def in_transaction?
182
+ @transaction_depth > 0
183
+ end
184
+
185
+ def enable_query_cache
186
+ @query_cache_enabled = true
187
+ @query_cache.clear
188
+ end
189
+
190
+ def disable_query_cache
191
+ @query_cache_enabled = false
192
+ @query_cache.clear
193
+ end
194
+
195
+ def clear_query_cache
196
+ @query_cache.clear
197
+ end
198
+
199
+ def quote(value, column = nil)
200
+ super
201
+ end
202
+
203
+ def quote_table_name(name)
204
+ super
205
+ end
206
+
207
+ def quote_column_name(name)
208
+ super
209
+ end
210
+
211
+ def type_for(column)
212
+ Type.to_rails(column.type)
213
+ end
214
+
215
+ def type_cast(value, type)
216
+ Type.serialize(value, type)
217
+ end
218
+
219
+ def log(sql, name = nil, &block)
220
+ start_time = Time.now
221
+ result = block.call
222
+ elapsed_ms = (Time.now - start_time) * 1000
223
+ @last_query_time = elapsed_ms
224
+
225
+ if @logger
226
+ @logger.debug " #{name || 'SQL'} (#{elapsed_ms.round(2)}ms) #{sql}"
227
+ end
228
+
229
+ result
230
+ end
231
+
232
+ def stats
233
+ {
234
+ connected: @connected,
235
+ transaction_depth: @transaction_depth,
236
+ statements: @statements.size,
237
+ query_cache_size: @query_cache.size,
238
+ query_cache_enabled: @query_cache_enabled,
239
+ last_query_time: @last_query_time
240
+ }
241
+ end
242
+
243
+ private
244
+
245
+ def execute_embedded(sql, params)
246
+ bound_sql = bind_parameters(sql, params)
247
+ statements = RubyDB::SQL::Parser.new(RubyDB::SQL::Lexer.new(bound_sql).tokenize).parse
248
+ results = statements.map do |statement|
249
+ plan = RubyDB::Execution::Planner.new(@engine).plan(statement)
250
+ RubyDB::Execution::Executor.new(@engine).execute(plan)
251
+ end
252
+ results.size == 1 ? results.first : { rows: results, row_count: results.size }
253
+ end
254
+
255
+ def bind_parameters(sql, params)
256
+ return sql if params.empty?
257
+
258
+ index = 0
259
+ used_indices = []
260
+ quoted = false
261
+ result = +""
262
+ position = 0
263
+
264
+ while position < sql.length
265
+ char = sql[position]
266
+ if char == "'"
267
+ if quoted && sql[position + 1] == "'"
268
+ result << "''"
269
+ position += 2
270
+ next
271
+ end
272
+ quoted = !quoted
273
+ result << char
274
+ elsif char == "?" && !quoted
275
+ raise ArgumentError, "Not enough bind parameters" if index >= params.size
276
+
277
+ result << quote(params[index])
278
+ used_indices << index
279
+ index += 1
280
+ elsif char == "$" && !quoted && (placeholder = sql[position..].match(/\A\$(\d+)/))
281
+ parameter_index = placeholder[1].to_i - 1
282
+ raise ArgumentError, "Bind parameter #{parameter_index + 1} is out of range" if parameter_index.negative? || parameter_index >= params.size
283
+
284
+ result << quote(params[parameter_index])
285
+ used_indices << parameter_index
286
+ position += placeholder[0].length - 1
287
+ else
288
+ result << char
289
+ end
290
+ position += 1
291
+ end
292
+ raise ArgumentError, "Too many bind parameters" unless used_indices.uniq.size == params.size
293
+
294
+ result
295
+ end
296
+
297
+ def ensure_connected
298
+ connect unless @connected
299
+ raise ConnectionError, "Not connected" unless @connected
300
+ end
301
+
302
+ def embedded_config?
303
+ @config[:embedded] || @config["embedded"]
304
+ end
305
+
306
+ def build_embedded_engine
307
+ database = @config[:database] || @config["database"]
308
+ raise ConnectionError, "Embedded RubyDB requires a database path" if database.nil? || database.to_s.empty?
309
+
310
+ RubyDB::Storage::Engine.new(database.to_s, auto_cleanup: false, auto_vacuum: false)
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Rails
5
+ # DatabaseStatements - Database statement methods for Rails
6
+ module DatabaseStatements
7
+ def execute(sql, name = nil)
8
+ log(sql, name) do
9
+ @connection.execute(sql)
10
+ end
11
+ end
12
+
13
+ def exec_query(sql, name = nil, binds = [])
14
+ log(sql, name) do
15
+ # Process binds
16
+ params = binds.map { |bind| bind.value }
17
+ @connection.execute(sql, params)
18
+ end
19
+ end
20
+
21
+ def exec_delete(sql, name = nil, binds = [])
22
+ log(sql, name) do
23
+ params = binds.map { |bind| bind.value }
24
+ result = @connection.execute(sql, params)
25
+ result.affected_rows
26
+ end
27
+ end
28
+
29
+ def exec_update(sql, name = nil, binds = [])
30
+ log(sql, name) do
31
+ params = binds.map { |bind| bind.value }
32
+ result = @connection.execute(sql, params)
33
+ result.affected_rows
34
+ end
35
+ end
36
+
37
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
38
+ log(sql, name) do
39
+ params = binds.map { |bind| bind.value }
40
+ result = @connection.execute(sql, params)
41
+ {
42
+ row_count: result.affected_rows,
43
+ last_insert_id: result.row_id || (result.first && result.first["id"])
44
+ }
45
+ end
46
+ end
47
+
48
+ def exec_query_with_result(sql, name = nil, binds = [])
49
+ exec_query(sql, name, binds)
50
+ end
51
+
52
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
53
+ result = exec_insert(sql, name, binds, pk, sequence_name)
54
+ id_value || result[:last_insert_id]
55
+ end
56
+
57
+ def update(sql, name = nil, binds = [])
58
+ exec_update(sql, name, binds)
59
+ end
60
+
61
+ def delete(sql, name = nil, binds = [])
62
+ exec_delete(sql, name, binds)
63
+ end
64
+
65
+ def select_all(sql, name = nil, binds = [])
66
+ exec_query(sql, name, binds)
67
+ end
68
+
69
+ def select_one(sql, name = nil, binds = [])
70
+ result = exec_query(sql, name, binds)
71
+ result.first
72
+ end
73
+
74
+ def select_value(sql, name = nil, binds = [])
75
+ result = exec_query(sql, name, binds)
76
+ result.first&.values&.first
77
+ end
78
+
79
+ def select_values(sql, name = nil, binds = [])
80
+ result = exec_query(sql, name, binds)
81
+ result.map { |row| row.values.first }
82
+ end
83
+
84
+ def select_rows(sql, name = nil, binds = [])
85
+ result = exec_query(sql, name, binds)
86
+ result.map { |row| row.values }
87
+ end
88
+
89
+ def begin_db_transaction
90
+ @connection.begin_db_transaction
91
+ end
92
+
93
+ def commit_db_transaction
94
+ @connection.commit_db_transaction
95
+ end
96
+
97
+ def rollback_db_transaction
98
+ @connection.rollback_db_transaction
99
+ end
100
+
101
+ def in_transaction?
102
+ @connection.in_transaction?
103
+ end
104
+
105
+ def transaction_joinable?
106
+ true
107
+ end
108
+
109
+ def transactional?
110
+ true
111
+ end
112
+
113
+ def supports_transactions?
114
+ true
115
+ end
116
+
117
+ def transaction_state
118
+ @connection.transaction_state
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Rails
5
+ # Migration - Rails migration support
6
+ class Migration
7
+ attr_reader :version, :name, :connection, :direction
8
+
9
+ def initialize(version, name, connection)
10
+ @version = version
11
+ @name = name
12
+ @connection = connection
13
+ @direction = :up
14
+ @migrated = false
15
+ end
16
+
17
+ def up(&block)
18
+ @direction = :up
19
+ instance_eval(&block) if block_given?
20
+ @migrated = true
21
+ end
22
+
23
+ def down(&block)
24
+ @direction = :down
25
+ instance_eval(&block) if block_given?
26
+ @migrated = true
27
+ end
28
+
29
+ def migrate(direction = :up)
30
+ @direction = direction
31
+ if direction == :up
32
+ up { yield if block_given? }
33
+ else
34
+ down { yield if block_given? }
35
+ end
36
+ end
37
+
38
+ def method_missing(method, *args, &block)
39
+ if @connection.respond_to?(method)
40
+ @connection.send(method, *args, &block)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ def respond_to_missing?(method, include_private = false)
47
+ @connection.respond_to?(method) || super
48
+ end
49
+
50
+ def create_table(table_name, options = {}, &block)
51
+ @connection.create_table(table_name, options, &block)
52
+ end
53
+
54
+ def drop_table(table_name, options = {})
55
+ @connection.drop_table(table_name, options)
56
+ end
57
+
58
+ def add_column(table_name, column_name, type, options = {})
59
+ @connection.add_column(table_name, column_name, type, options)
60
+ end
61
+
62
+ def remove_column(table_name, column_name, options = {})
63
+ @connection.remove_column(table_name, column_name, options)
64
+ end
65
+
66
+ def change_column(table_name, column_name, type, options = {})
67
+ @connection.change_column(table_name, column_name, type, options)
68
+ end
69
+
70
+ def rename_column(table_name, old_name, new_name)
71
+ @connection.rename_column(table_name, old_name, new_name)
72
+ end
73
+
74
+ def add_index(table_name, column_name, options = {})
75
+ @connection.add_index(table_name, column_name, options)
76
+ end
77
+
78
+ def remove_index(table_name, options = {})
79
+ @connection.remove_index(table_name, options)
80
+ end
81
+
82
+ def add_foreign_key(from_table, to_table, options = {})
83
+ @connection.add_foreign_key(from_table, to_table, options)
84
+ end
85
+
86
+ def remove_foreign_key(from_table, options = {})
87
+ @connection.remove_foreign_key(from_table, options)
88
+ end
89
+
90
+ def add_timestamps(table_name, options = {})
91
+ @connection.add_timestamps(table_name, options)
92
+ end
93
+
94
+ def remove_timestamps(table_name, options = {})
95
+ @connection.remove_timestamps(table_name, options)
96
+ end
97
+
98
+ def change_column_null(table_name, column_name, null, default = nil)
99
+ @connection.change_column_null(table_name, column_name, null, default)
100
+ end
101
+
102
+ def change_column_default(table_name, column_name, default)
103
+ @connection.change_column_default(table_name, column_name, default)
104
+ end
105
+
106
+ def execute(sql)
107
+ @connection.execute(sql)
108
+ end
109
+
110
+ def quote(value)
111
+ @connection.quote(value)
112
+ end
113
+
114
+ def quote_table_name(name)
115
+ @connection.quote_table_name(name)
116
+ end
117
+
118
+ def quote_column_name(name)
119
+ @connection.quote_column_name(name)
120
+ end
121
+
122
+ def migrate
123
+ @migrated
124
+ end
125
+
126
+ def to_s
127
+ "Migration #{@version} #{@name} (#{@direction})"
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Rails
5
+ # Quoting - Handles SQL quoting
6
+ module Quoting
7
+ QUOTE_CHARACTER = "'"
8
+ ESCAPE_CHARACTER = "\\"
9
+ IDENTIFIER_QUOTE = "\""
10
+
11
+ def quote(value, column = nil)
12
+ return "NULL" if value.nil?
13
+
14
+ case value
15
+ when String
16
+ quote_string(value)
17
+ when Numeric
18
+ value.to_s
19
+ when TrueClass
20
+ "TRUE"
21
+ when FalseClass
22
+ "FALSE"
23
+ when Date, Time, DateTime
24
+ quote_string(value.iso8601)
25
+ when Symbol
26
+ quote_string(value.to_s)
27
+ when Array
28
+ value.map { |v| quote(v) }.join(", ")
29
+ else
30
+ quote_string(value.to_s)
31
+ end
32
+ end
33
+
34
+ def quote_string(string)
35
+ "'#{string.gsub("'", "''")}'"
36
+ end
37
+
38
+ def quote_table_name(name)
39
+ "\"#{name.to_s}\""
40
+ end
41
+
42
+ def quote_column_name(name)
43
+ "\"#{name.to_s}\""
44
+ end
45
+
46
+ def quote_table_name_if_needed(name)
47
+ name = name.to_s
48
+ return name if name.start_with?("\"") && name.end_with?("\"")
49
+ quote_table_name(name)
50
+ end
51
+
52
+ def quote_column_name_if_needed(name)
53
+ name = name.to_s
54
+ return name if name.start_with?("\"") && name.end_with?("\"")
55
+ quote_column_name(name)
56
+ end
57
+
58
+ def quote_identifier(name)
59
+ name = name.to_s
60
+ return name if name =~ /^[a-zA-Z_][a-zA-Z0-9_]*$/
61
+ "\"#{name.gsub('"', '""')}\""
62
+ end
63
+
64
+ def quote_default(value)
65
+ case value
66
+ when nil
67
+ "NULL"
68
+ when String
69
+ quote_string(value)
70
+ when Numeric
71
+ value.to_s
72
+ when TrueClass
73
+ "TRUE"
74
+ when FalseClass
75
+ "FALSE"
76
+ when Date, Time, DateTime
77
+ quote_string(value.iso8601)
78
+ else
79
+ quote_string(value.to_s)
80
+ end
81
+ end
82
+
83
+ def unquote(string)
84
+ return nil if string.nil? || string == "NULL"
85
+ return string[1..-2] if string.start_with?("'") && string.end_with?("'")
86
+ string
87
+ end
88
+
89
+ private
90
+
91
+ def quote_bound_value(value)
92
+ case value
93
+ when String
94
+ quote_string(value)
95
+ when Numeric
96
+ value
97
+ when TrueClass
98
+ 1
99
+ when FalseClass
100
+ 0
101
+ when Date, Time, DateTime
102
+ quote_string(value.iso8601)
103
+ else
104
+ quote_string(value.to_s)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end