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,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Rails
5
+ # Result - Query result adapter for Rails
6
+ class Result
7
+ include Enumerable
8
+
9
+ attr_reader :columns, :rows, :row_count, :affected_rows
10
+ attr_reader :command_tag, :statement_id, :row_id
11
+
12
+ def initialize(result)
13
+ @columns = result[:columns] || result[:column_names] || []
14
+ @rows = result[:rows] || []
15
+ @row_count = result[:row_count] || @rows.size
16
+ @affected_rows = result[:affected_rows] || 0
17
+ @command_tag = result[:command_tag] || "SELECT"
18
+ @statement_id = result[:statement_id]
19
+ @row_id = result[:row_id]
20
+ @transaction_id = result[:transaction_id]
21
+ @success = result[:success] != false
22
+ @error = result[:error]
23
+ @execution_time = result[:execution_time_ms] || 0
24
+ @position = 0
25
+ end
26
+
27
+ def success?
28
+ @success
29
+ end
30
+
31
+ def error?
32
+ @success == false && !@error.nil?
33
+ end
34
+
35
+ def empty?
36
+ @rows.empty?
37
+ end
38
+
39
+ def first
40
+ @rows.first
41
+ end
42
+
43
+ def last
44
+ @rows.last
45
+ end
46
+
47
+ def [](index)
48
+ @rows[index]
49
+ end
50
+
51
+ def each(&block)
52
+ @rows.each(&block)
53
+ end
54
+
55
+ def each_with_index(&block)
56
+ @rows.each_with_index(&block)
57
+ end
58
+
59
+ def each_row(&block)
60
+ @rows.each(&block)
61
+ end
62
+
63
+ def each_column(&block)
64
+ @columns.each(&block)
65
+ end
66
+
67
+ def column_names
68
+ @columns.map do |column|
69
+ column.is_a?(Hash) ? (column[:name] || column["name"] || column) : column
70
+ end
71
+ end
72
+
73
+ def column_types
74
+ @columns.map { |c| c[:type] || "text" }
75
+ end
76
+
77
+ def to_a
78
+ @rows
79
+ end
80
+
81
+ def to_hash
82
+ {
83
+ columns: @columns,
84
+ rows: @rows,
85
+ row_count: @row_count,
86
+ affected_rows: @affected_rows,
87
+ command_tag: @command_tag,
88
+ success: @success,
89
+ error: @error,
90
+ execution_time_ms: @execution_time
91
+ }
92
+ end
93
+
94
+ def to_ary
95
+ @rows
96
+ end
97
+
98
+ def size
99
+ @row_count
100
+ end
101
+
102
+ alias length size
103
+ alias count size
104
+
105
+ def inspect
106
+ rows_preview = @rows.first(3).map(&:inspect).join(", ")
107
+ more = @rows.size > 3 ? "..." : ""
108
+ "#<Result rows=#{@row_count} columns=#{@columns.size} data=[#{rows_preview}#{more}]>"
109
+ end
110
+
111
+ def to_s
112
+ if @rows.empty?
113
+ "Result: #{@command_tag} (0 rows)"
114
+ else
115
+ "Result: #{@command_tag} (#{@row_count} rows)"
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,339 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Rails
5
+ # SchemaStatements - Schema statement methods for Rails
6
+ module SchemaStatements
7
+ # Collects a Rails-style create_table block into executable SQL.
8
+ class TableDefinition
9
+ TYPE_METHODS = %i[bigint binary boolean date datetime decimal float integer json smallint string text time timestamp uuid].freeze
10
+ attr_reader :columns, :constraints
11
+
12
+ def initialize(adapter, table_name, options = {})
13
+ @adapter = adapter
14
+ @table_name = table_name
15
+ @options = options
16
+ @columns = []
17
+ @constraints = []
18
+ end
19
+
20
+ def column(name, type, options = {})
21
+ @columns << RubyDB::Catalog::Column.new(name, type, **options)
22
+ self
23
+ end
24
+
25
+ def timestamps(options = {})
26
+ column(:created_at, :timestamp, options)
27
+ column(:updated_at, :timestamp, options)
28
+ end
29
+
30
+ def primary_key(name = :id, type = :integer, options = {})
31
+ column(name, type, options.merge(primary_key: true, null: false))
32
+ end
33
+
34
+ # Support Rails' common `t.references :repository, foreign_key: true`
35
+ # shorthand while keeping the generated SQL within RubyDB's schema
36
+ # contract. Index creation remains an explicit `add_index` operation.
37
+ def references(name, options = {})
38
+ reference_name = name.to_s
39
+ column_name = options[:column] || "#{reference_name}_id"
40
+ column_options = options.slice(:null, :default, :limit, :precision, :scale)
41
+ column(column_name, options[:type] || :integer, column_options)
42
+
43
+ return self unless options[:foreign_key]
44
+
45
+ reference_table = options[:to_table] || pluralize_reference(reference_name)
46
+ @constraints << RubyDB::Constraints::ForeignKeyConstraint.new(
47
+ @table_name,
48
+ column_name,
49
+ reference_table,
50
+ options[:primary_key] || :id,
51
+ options.slice(:name, :on_delete, :on_update)
52
+ )
53
+ self
54
+ end
55
+
56
+ def pluralize_reference(name)
57
+ return name if name.end_with?("s")
58
+ return "#{name[0...-1]}ies" if name.end_with?("y")
59
+
60
+ "#{name}s"
61
+ end
62
+ private :pluralize_reference
63
+
64
+ def method_missing(method, *args, &block)
65
+ if TYPE_METHODS.include?(method)
66
+ name = args.shift
67
+ options = args.shift || {}
68
+ return column(name, method, options)
69
+ end
70
+ super
71
+ end
72
+
73
+ def respond_to_missing?(method, include_private = false)
74
+ TYPE_METHODS.include?(method) || method == :references || super
75
+ end
76
+ end
77
+
78
+ def create_table(table_name, options = {})
79
+ columns = []
80
+ constraints = []
81
+
82
+ # Rails creates an integer primary key unless a migration explicitly
83
+ # requests id: false. Keep the same default for both the standalone
84
+ # RubyDB adapter and ActiveRecord's migration DSL.
85
+ unless options[:id] == false
86
+ primary_key_name = options[:primary_key] || :id
87
+ primary_key_type = options[:id].is_a?(Symbol) ? options[:id] : :integer
88
+ columns << RubyDB::Catalog::Column.new(primary_key_name, primary_key_type, primary_key: true, null: false)
89
+ end
90
+
91
+ if block_given?
92
+ table_definition = TableDefinition.new(self, table_name, options)
93
+ yield table_definition
94
+ columns.concat(table_definition.columns)
95
+ constraints = table_definition.constraints
96
+ end
97
+
98
+ sql = build_create_table_sql(table_name, columns, constraints, options)
99
+ execute(sql)
100
+ end
101
+
102
+ def drop_table(table_name, options = {})
103
+ sql = "DROP TABLE"
104
+ sql << " IF EXISTS" if options[:if_exists]
105
+ sql << " #{quote_table_name(table_name)}"
106
+ sql << " CASCADE" if options[:cascade]
107
+ execute(sql)
108
+ end
109
+
110
+ def add_column(table_name, column_name, type, options = {})
111
+ sql = "ALTER TABLE #{quote_table_name(table_name)}"
112
+ sql << " ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options)}"
113
+
114
+ if options[:null] == false
115
+ sql << " NOT NULL"
116
+ end
117
+
118
+ if options.key?(:default)
119
+ sql << " DEFAULT #{quote_default(options[:default])}"
120
+ end
121
+
122
+ if options[:primary_key]
123
+ sql << " PRIMARY KEY"
124
+ end
125
+
126
+ execute(sql)
127
+ end
128
+
129
+ def remove_column(table_name, column_name, type = nil, options = {})
130
+ sql = "ALTER TABLE #{quote_table_name(table_name)}"
131
+ sql << " DROP COLUMN #{quote_column_name(column_name)}"
132
+ sql << " CASCADE" if options[:cascade]
133
+ execute(sql)
134
+ end
135
+
136
+ def change_column(table_name, column_name, type, options = {})
137
+ sql = "ALTER TABLE #{quote_table_name(table_name)}"
138
+ sql << " ALTER COLUMN #{quote_column_name(column_name)}"
139
+ sql << " TYPE #{type_to_sql(type, options)}"
140
+ execute(sql)
141
+
142
+ if options[:null] == false
143
+ sql = "ALTER TABLE #{quote_table_name(table_name)}"
144
+ sql << " ALTER COLUMN #{quote_column_name(column_name)} SET NOT NULL"
145
+ execute(sql)
146
+ elsif options[:null] == true
147
+ sql = "ALTER TABLE #{quote_table_name(table_name)}"
148
+ sql << " ALTER COLUMN #{quote_column_name(column_name)} DROP NOT NULL"
149
+ execute(sql)
150
+ end
151
+
152
+ return unless options.key?(:default)
153
+
154
+ sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)}"
155
+ if options[:default].nil?
156
+ sql << " DROP DEFAULT"
157
+ else
158
+ sql << " SET DEFAULT #{quote_default(options[:default])}"
159
+ end
160
+ execute(sql)
161
+ end
162
+
163
+ def rename_column(table_name, column_name, new_column_name)
164
+ sql = "ALTER TABLE #{quote_table_name(table_name)}"
165
+ sql << " RENAME COLUMN #{quote_column_name(column_name)}"
166
+ sql << " TO #{quote_column_name(new_column_name)}"
167
+ execute(sql)
168
+ end
169
+
170
+ def add_index(table_name, column_name, options = {})
171
+ index_name = options[:name] || "idx_#{table_name}_#{Array(column_name).join('_')}"
172
+ sql = +"CREATE"
173
+ sql << " UNIQUE" if options[:unique]
174
+ sql << " INDEX #{quote_column_name(index_name)}"
175
+ sql << " ON #{quote_table_name(table_name)}"
176
+ sql << " (#{Array(column_name).map { |c| quote_column_name(c) }.join(', ')})"
177
+ execute(sql)
178
+ end
179
+
180
+ def remove_index(table_name, options = {})
181
+ index_name = options[:name]
182
+ if index_name.nil?
183
+ column_name = options[:column] || options[:columns]
184
+ index_name = "idx_#{table_name}_#{Array(column_name).join('_')}"
185
+ end
186
+
187
+ sql = "DROP INDEX #{quote_column_name(index_name)}"
188
+ sql << " ON #{quote_table_name(table_name)}"
189
+ execute(sql)
190
+ end
191
+
192
+ def add_foreign_key(from_table, to_table, options = {})
193
+ fk_name = options[:name] || "fk_#{from_table}_to_#{to_table}"
194
+ sql = "ALTER TABLE #{quote_table_name(from_table)}"
195
+ sql << " ADD CONSTRAINT #{quote_column_name(fk_name)}"
196
+ column = options[:column] || "#{to_table.to_s.sub(/s\z/, '')}_id"
197
+ sql << " FOREIGN KEY (#{quote_column_name(column)})"
198
+ sql << " REFERENCES #{quote_table_name(to_table)}"
199
+ sql << " (#{quote_column_name(options[:primary_key] || :id)})"
200
+ execute(sql)
201
+ end
202
+
203
+ def remove_foreign_key(from_table, options = {})
204
+ fk_name = options[:name] || "fk_#{from_table}_to_#{options[:to_table]}"
205
+ sql = "ALTER TABLE #{quote_table_name(from_table)}"
206
+ sql << " DROP CONSTRAINT #{quote_column_name(fk_name)}"
207
+ execute(sql)
208
+ end
209
+
210
+ def add_timestamps(table_name, options = {})
211
+ add_column(table_name, :created_at, :timestamp, options)
212
+ add_column(table_name, :updated_at, :timestamp, options)
213
+ end
214
+
215
+ def remove_timestamps(table_name, options = {})
216
+ remove_column(table_name, :updated_at, nil, options)
217
+ remove_column(table_name, :created_at, nil, options)
218
+ end
219
+
220
+ def change_column_null(table_name, column_name, null, default = nil)
221
+ if default
222
+ sql = "UPDATE #{quote_table_name(table_name)}"
223
+ sql << " SET #{quote_column_name(column_name)} = #{quote(default)}"
224
+ sql << " WHERE #{quote_column_name(column_name)} IS NULL"
225
+ execute(sql)
226
+ end
227
+
228
+ sql = "ALTER TABLE #{quote_table_name(table_name)}"
229
+ sql << " ALTER COLUMN #{quote_column_name(column_name)}"
230
+ sql << (null ? " DROP" : " SET") + " NOT NULL"
231
+ execute(sql)
232
+ end
233
+
234
+ def change_column_default(table_name, column_name, default)
235
+ sql = "ALTER TABLE #{quote_table_name(table_name)}"
236
+ sql << " ALTER COLUMN #{quote_column_name(column_name)}"
237
+ if default.nil?
238
+ sql << " DROP DEFAULT"
239
+ else
240
+ sql << " SET DEFAULT #{quote(default)}"
241
+ end
242
+ execute(sql)
243
+ end
244
+
245
+ private
246
+
247
+ def type_to_sql(type, options = {})
248
+ case type.to_sym
249
+ when :integer
250
+ "INTEGER"
251
+ when :bigint
252
+ "BIGINT"
253
+ when :smallint
254
+ "SMALLINT"
255
+ when :float
256
+ "FLOAT"
257
+ when :decimal
258
+ precision = options[:precision] || 10
259
+ scale = options[:scale] || 2
260
+ "DECIMAL(#{precision}, #{scale})"
261
+ when :boolean
262
+ "BOOLEAN"
263
+ when :text
264
+ "TEXT"
265
+ when :string
266
+ limit = options[:limit] || 255
267
+ "VARCHAR(#{limit})"
268
+ when :binary
269
+ "BLOB"
270
+ when :date
271
+ "DATE"
272
+ when :time
273
+ "TIME"
274
+ when :datetime, :timestamp
275
+ "TIMESTAMP"
276
+ when :json
277
+ "JSON"
278
+ when :uuid
279
+ "UUID"
280
+ else
281
+ "TEXT"
282
+ end
283
+ end
284
+
285
+ def build_create_table_sql(table_name, columns, constraints, options)
286
+ sql = +"CREATE TABLE"
287
+ sql << " IF NOT EXISTS" if options[:if_not_exists]
288
+ sql << " #{quote_table_name(table_name)} ("
289
+
290
+ col_defs = columns.map do |col|
291
+ definition = "#{quote_column_name(col.name)} #{type_to_sql(col.type, col.options)}"
292
+ definition << " PRIMARY KEY" if col.options[:primary_key]
293
+ definition << " NOT NULL" if col.options[:null] == false
294
+ definition << " DEFAULT #{quote(col.options[:default])}" if col.options.key?(:default)
295
+ definition
296
+ end
297
+
298
+ constraints.each do |constraint|
299
+ col_defs << constraint.to_sql
300
+ end
301
+
302
+ sql << col_defs.join(", ")
303
+ sql << ")"
304
+ sql
305
+ end
306
+
307
+ def quote(value)
308
+ case value
309
+ when String
310
+ "'#{value.gsub("'", "''")}'"
311
+ when Numeric
312
+ value.to_s
313
+ when TrueClass
314
+ "TRUE"
315
+ when FalseClass
316
+ "FALSE"
317
+ when nil
318
+ "NULL"
319
+ when Date, Time, DateTime
320
+ "'#{value.iso8601}'"
321
+ else
322
+ "'#{value.to_s.gsub("'", "''")}'"
323
+ end
324
+ end
325
+
326
+ def quote_table_name(name)
327
+ "\"#{name.to_s}\""
328
+ end
329
+
330
+ def quote_column_name(name)
331
+ "\"#{name.to_s}\""
332
+ end
333
+
334
+ def quote_default(value)
335
+ quote(value)
336
+ end
337
+ end
338
+ end
339
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Rails
5
+ # Transaction - Transaction management for Rails
6
+ class Transaction
7
+ attr_reader :connection, :depth, :state
8
+
9
+ def initialize(connection)
10
+ @connection = connection
11
+ @depth = 0
12
+ @state = :active
13
+ @savepoints = []
14
+ @lock = Mutex.new
15
+ end
16
+
17
+ def begin
18
+ @lock.synchronize do
19
+ return false if @state != :active
20
+
21
+ @connection.begin_db_transaction
22
+ @depth += 1
23
+ true
24
+ end
25
+ end
26
+
27
+ def commit
28
+ @lock.synchronize do
29
+ return false if @state == :committed || @state == :rolled_back
30
+
31
+ @connection.commit_db_transaction
32
+ @depth -= 1
33
+ @state = :committed if @depth == 0
34
+ true
35
+ end
36
+ end
37
+
38
+ def rollback
39
+ @lock.synchronize do
40
+ return false if @state == :committed || @state == :rolled_back
41
+
42
+ @connection.rollback_db_transaction
43
+ @depth = 0
44
+ @state = :rolled_back
45
+ true
46
+ end
47
+ end
48
+
49
+ def create_savepoint(name)
50
+ @lock.synchronize do
51
+ return false if @state != :active
52
+
53
+ @connection.execute("SAVEPOINT #{name}")
54
+ @savepoints << name
55
+ true
56
+ end
57
+ end
58
+
59
+ def rollback_to_savepoint(name)
60
+ @lock.synchronize do
61
+ return false if @state != :active
62
+
63
+ unless @savepoints.include?(name)
64
+ return false
65
+ end
66
+
67
+ @connection.execute("ROLLBACK TO SAVEPOINT #{name}")
68
+ @savepoints = @savepoints[0...@savepoints.index(name)]
69
+ true
70
+ end
71
+ end
72
+
73
+ def release_savepoint(name)
74
+ @lock.synchronize do
75
+ return false if @state != :active
76
+
77
+ unless @savepoints.include?(name)
78
+ return false
79
+ end
80
+
81
+ @connection.execute("RELEASE SAVEPOINT #{name}")
82
+ @savepoints.delete(name)
83
+ true
84
+ end
85
+ end
86
+
87
+ def active?
88
+ @state == :active
89
+ end
90
+
91
+ def in_progress?
92
+ @depth > 0
93
+ end
94
+
95
+ def to_hash
96
+ {
97
+ depth: @depth,
98
+ state: @state,
99
+ savepoints: @savepoints,
100
+ in_progress: in_progress?
101
+ }
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Rails
5
+ # Type - Maps Ruby/Rails types to RubyDB types
6
+ class Type
7
+ TYPE_MAPPING = {
8
+ integer: :integer,
9
+ bigint: :bigint,
10
+ smallint: :smallint,
11
+ float: :float,
12
+ decimal: :decimal,
13
+ boolean: :boolean,
14
+ text: :text,
15
+ string: :varchar,
16
+ blob: :blob,
17
+ date: :date,
18
+ time: :time,
19
+ datetime: :timestamp,
20
+ timestamp: :timestamp,
21
+ json: :json,
22
+ uuid: :uuid,
23
+ primary_key: :integer,
24
+ references: :integer
25
+ }
26
+
27
+ RUBYDB_TO_RAILS = {
28
+ integer: :integer,
29
+ bigint: :bigint,
30
+ smallint: :smallint,
31
+ float: :float,
32
+ decimal: :decimal,
33
+ boolean: :boolean,
34
+ text: :text,
35
+ varchar: :string,
36
+ char: :string,
37
+ blob: :binary,
38
+ date: :date,
39
+ time: :time,
40
+ timestamp: :datetime,
41
+ json: :json,
42
+ uuid: :uuid,
43
+ null: :null
44
+ }
45
+
46
+ def self.to_rubydb(rails_type, options = {})
47
+ type = TYPE_MAPPING[rails_type.to_sym]
48
+ return type if type
49
+
50
+ # Handle special cases
51
+ case rails_type.to_s
52
+ when "primary_key"
53
+ :integer
54
+ when "references"
55
+ :integer
56
+ when "jsonb"
57
+ :json
58
+ when "binary"
59
+ :blob
60
+ when "datetime"
61
+ :timestamp
62
+ when "timestamp"
63
+ :timestamp
64
+ else
65
+ :text
66
+ end
67
+ end
68
+
69
+ def self.to_rails(rubydb_type)
70
+ RUBYDB_TO_RAILS[rubydb_type.to_sym] || :string
71
+ end
72
+
73
+ def self.native_database_types
74
+ {
75
+ primary_key: "INTEGER PRIMARY KEY AUTOINCREMENT",
76
+ string: { name: "VARCHAR", limit: 255 },
77
+ text: { name: "TEXT" },
78
+ integer: { name: "INTEGER" },
79
+ bigint: { name: "BIGINT" },
80
+ float: { name: "FLOAT" },
81
+ decimal: { name: "DECIMAL", precision: 10, scale: 2 },
82
+ datetime: { name: "TIMESTAMP" },
83
+ timestamp: { name: "TIMESTAMP" },
84
+ time: { name: "TIME" },
85
+ date: { name: "DATE" },
86
+ binary: { name: "BLOB" },
87
+ boolean: { name: "BOOLEAN" },
88
+ json: { name: "JSON" },
89
+ uuid: { name: "UUID" }
90
+ }
91
+ end
92
+
93
+ def self.serialize(value, type)
94
+ case type.to_sym
95
+ when :integer, :bigint, :smallint
96
+ value.to_i if value
97
+ when :float, :decimal
98
+ value.to_f if value
99
+ when :boolean
100
+ !!value
101
+ when :json
102
+ value.is_a?(String) ? JSON.parse(value) : value
103
+ when :timestamp, :datetime
104
+ value.is_a?(String) ? Time.parse(value) : value
105
+ when :date
106
+ value.is_a?(String) ? Date.parse(value) : value
107
+ else
108
+ value
109
+ end
110
+ end
111
+
112
+ def self.deserialize(value, type)
113
+ case type.to_sym
114
+ when :json
115
+ value.is_a?(Hash) ? value.to_json : value
116
+ when :timestamp, :datetime
117
+ value.is_a?(Time) ? value.iso8601 : value
118
+ when :date
119
+ value.is_a?(Date) ? value.iso8601 : value
120
+ else
121
+ value
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end