rubydb 0.1.4 → 0.1.6

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 (469) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +15 -15
  3. data/.github/workflows/benchmark.yml +26 -26
  4. data/.github/workflows/compatibility.yml +63 -63
  5. data/.github/workflows/fuzz.yml +33 -33
  6. data/.github/workflows/lint.yml +21 -21
  7. data/.github/workflows/operations.yml +24 -24
  8. data/.github/workflows/production-validation.yml +111 -111
  9. data/.github/workflows/release.yml +77 -77
  10. data/.github/workflows/security.yml +39 -37
  11. data/.github/workflows/test.yml +26 -26
  12. data/.github/workflows/workload.yml +58 -58
  13. data/.gitignore +16 -5
  14. data/.rubocop.yml +50 -44
  15. data/.standard.yml +9 -14
  16. data/ARCHITECTURE.md +21 -21
  17. data/CHANGELOG.md +57 -27
  18. data/CODE_OF_CONDUCT.md +13 -13
  19. data/CONTRIBUTING.md +29 -29
  20. data/GOVERNANCE.md +16 -16
  21. data/Gemfile +18 -17
  22. data/Gemfile.lock +125 -71
  23. data/README.md +168 -12
  24. data/ROADMAP.md +27 -27
  25. data/Rakefile +76 -71
  26. data/SECURITY.md +54 -54
  27. data/SUPPORT.md +14 -14
  28. data/accelerator/bin/SHA256SUMS +6 -0
  29. data/accelerator/bin/rubydb-accelerator-darwin-amd64 +0 -0
  30. data/accelerator/bin/rubydb-accelerator-darwin-arm64 +0 -0
  31. data/accelerator/bin/rubydb-accelerator-linux-amd64 +0 -0
  32. data/accelerator/bin/rubydb-accelerator-linux-arm64 +0 -0
  33. data/accelerator/bin/rubydb-accelerator-windows-amd64.exe +0 -0
  34. data/accelerator/bin/rubydb-accelerator-windows-arm64.exe +0 -0
  35. data/accelerator/cmd/rubydb-accelerator/main.go +11 -0
  36. data/accelerator/go.mod +3 -0
  37. data/accelerator/internal/execution/aggregate.go +94 -0
  38. data/accelerator/internal/execution/distinct.go +22 -0
  39. data/accelerator/internal/execution/filter.go +73 -0
  40. data/accelerator/internal/execution/join.go +79 -0
  41. data/accelerator/internal/execution/operators.go +167 -0
  42. data/accelerator/internal/execution/scan.go +20 -0
  43. data/accelerator/internal/execution/sort.go +62 -0
  44. data/accelerator/internal/execution/types.go +136 -0
  45. data/accelerator/internal/execution/value.go +67 -0
  46. data/accelerator/internal/memory/arena.go +47 -0
  47. data/accelerator/internal/memory/reuse.go +22 -0
  48. data/accelerator/internal/metrics/registry.go +67 -0
  49. data/accelerator/internal/parallel/bounded_queue.go +56 -0
  50. data/accelerator/internal/parallel/scheduler.go +47 -0
  51. data/accelerator/internal/parallel/worker_pool.go +53 -0
  52. data/accelerator/internal/protocol/cancellation.go +48 -0
  53. data/accelerator/internal/protocol/columnar.go +263 -0
  54. data/accelerator/internal/protocol/frame.go +187 -0
  55. data/accelerator/internal/runtime/worker.go +521 -0
  56. data/accelerator/internal/storage/page_reader.go +81 -0
  57. data/accelerator/internal/storage/snapshot_scan.go +539 -0
  58. data/accelerator/internal/wal/checksum.go +13 -0
  59. data/accelerator/internal/wal/compression.go +41 -0
  60. data/accelerator/internal/wal/group_commit.go +24 -0
  61. data/accelerator/internal/wal/record_encoder.go +40 -0
  62. data/adapters/activerecord/Gemfile +11 -11
  63. data/adapters/activerecord/README.md +8 -3
  64. data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +881 -879
  65. data/adapters/activerecord/rubydb-activerecord.gemspec +21 -21
  66. data/adapters/activerecord/spec/rubydb_adapter_integration_spec.rb +143 -143
  67. data/adapters/ruby/README.md +18 -18
  68. data/adapters/sequel/README.md +11 -11
  69. data/config/monitoring/prometheus-alerts.yml +39 -39
  70. data/config/production.yml +36 -36
  71. data/docs/README.md +77 -71
  72. data/docs/architecture/concurrency.md +14 -14
  73. data/docs/architecture/current-state.md +125 -125
  74. data/docs/architecture/execution-engine.md +25 -25
  75. data/docs/architecture/go-accelerator.md +179 -0
  76. data/docs/architecture/indexes.md +19 -19
  77. data/docs/architecture/mvcc.md +19 -19
  78. data/docs/architecture/overview.md +13 -13
  79. data/docs/architecture/pages.md +11 -11
  80. data/docs/architecture/production-roadmap.md +82 -82
  81. data/docs/architecture/query-planner.md +20 -20
  82. data/docs/architecture/recovery.md +18 -18
  83. data/docs/architecture/sql-engine.md +12 -12
  84. data/docs/architecture/storage-engine.md +14 -14
  85. data/docs/architecture/transactions.md +10 -10
  86. data/docs/architecture/wal.md +28 -28
  87. data/docs/cli-cheatsheet.md +98 -98
  88. data/docs/cli.md +299 -275
  89. data/docs/contributing/architecture.md +9 -9
  90. data/docs/contributing/benchmarking.md +30 -14
  91. data/docs/contributing/development.md +16 -16
  92. data/docs/contributing/release-process.md +49 -49
  93. data/docs/contributing/testing.md +16 -16
  94. data/docs/debugging.md +229 -229
  95. data/docs/developer/branching.md +10 -10
  96. data/docs/developer/database-diff.md +10 -10
  97. data/docs/developer/local-development.md +49 -17
  98. data/docs/developer/snapshots.md +9 -9
  99. data/docs/developer/temporal-data.md +10 -10
  100. data/docs/developer-guide.md +297 -297
  101. data/docs/getting-started/first-database.md +16 -16
  102. data/docs/getting-started/first-query.md +13 -13
  103. data/docs/getting-started/installation.md +19 -19
  104. data/docs/getting-started/local-to-production.md +300 -300
  105. data/docs/getting-started/quickstart.md +17 -17
  106. data/docs/getting-started/rails.md +16 -16
  107. data/docs/hardening_backlog.md +93 -93
  108. data/docs/lessons-learned.md +112 -112
  109. data/docs/operations/backups.md +33 -33
  110. data/docs/operations/disaster-recovery.md +31 -31
  111. data/docs/operations/failover.md +30 -30
  112. data/docs/operations/monitoring.md +25 -25
  113. data/docs/operations/production-guide.md +295 -295
  114. data/docs/operations/production-runbook.md +45 -45
  115. data/docs/operations/replication.md +33 -33
  116. data/docs/operations/restore.md +6 -6
  117. data/docs/operations/runbook.md +34 -34
  118. data/docs/operations/upgrades.md +14 -14
  119. data/docs/operations/workload-testing.md +17 -17
  120. data/docs/production-readiness.md +118 -118
  121. data/docs/production_validation.md +150 -150
  122. data/docs/rails/active-record.md +11 -11
  123. data/docs/rails/compatibility-guide.md +90 -90
  124. data/docs/rails/database-yml.md +92 -92
  125. data/docs/rails/installation.md +17 -17
  126. data/docs/rails/migrations.md +17 -17
  127. data/docs/rails/production.md +82 -82
  128. data/docs/rails/troubleshooting.md +18 -18
  129. data/docs/release.md +25 -25
  130. data/docs/server/architecture.md +10 -10
  131. data/docs/server/authentication.md +10 -10
  132. data/docs/server/configuration.md +16 -16
  133. data/docs/server/connection-pooling.md +10 -10
  134. data/docs/server/deployment.md +10 -10
  135. data/docs/server/protocol.md +12 -12
  136. data/docs/sql/compatibility-guide.md +82 -82
  137. data/docs/sql/compatibility.md +39 -39
  138. data/docs/sql/data-types.md +10 -10
  139. data/docs/sql/functions.md +9 -9
  140. data/docs/sql/joins.md +9 -9
  141. data/docs/sql/operators.md +9 -9
  142. data/docs/sql/sqlite-compatibility.md +21 -21
  143. data/docs/sql/syntax.md +10 -10
  144. data/docs/sql/transactions.md +10 -10
  145. data/docs/troubleshooting.md +244 -244
  146. data/lessons/01-foundations.md +73 -0
  147. data/lessons/02-local-development.md +121 -0
  148. data/lessons/03-embedded-rubydb.md +99 -0
  149. data/lessons/04-rails-complex-apps.md +138 -0
  150. data/lessons/05-rubydb-production-server.md +237 -0
  151. data/lessons/06-postgresql-massive-apps.md +96 -0
  152. data/lessons/07-hybrid-microservices.md +179 -0
  153. data/lessons/08-migrations-backups-recovery.md +86 -0
  154. data/lessons/09-observability-security-scale.md +87 -0
  155. data/lessons/10-release-readiness.md +192 -0
  156. data/lessons/11-community-adapter.md +323 -0
  157. data/lessons/12-rails-ecommerce-pressure.md +263 -0
  158. data/lib/rubydb/accelerator/client.rb +451 -0
  159. data/lib/rubydb/accelerator/error.rb +22 -0
  160. data/lib/rubydb/accelerator/manager.rb +606 -0
  161. data/lib/rubydb/accelerator.rb +13 -0
  162. data/lib/rubydb/backup/archive.rb +332 -334
  163. data/lib/rubydb/backup/backup.rb +400 -401
  164. data/lib/rubydb/backup/incremental.rb +349 -353
  165. data/lib/rubydb/backup/restore.rb +289 -290
  166. data/lib/rubydb/backup/snapshot.rb +265 -267
  167. data/lib/rubydb/backup/verification.rb +276 -279
  168. data/lib/rubydb/branching/branch.rb +181 -181
  169. data/lib/rubydb/branching/branch_manager.rb +307 -311
  170. data/lib/rubydb/branching/branch_metadata.rb +140 -140
  171. data/lib/rubydb/branching/checkout.rb +165 -166
  172. data/lib/rubydb/branching/copy_on_write.rb +272 -272
  173. data/lib/rubydb/branching/diff.rb +137 -138
  174. data/lib/rubydb/branching/merge.rb +282 -285
  175. data/lib/rubydb/build_info.rb +15 -15
  176. data/lib/rubydb/catalog/catalog.rb +391 -391
  177. data/lib/rubydb/catalog/column.rb +112 -112
  178. data/lib/rubydb/catalog/constraint.rb +180 -180
  179. data/lib/rubydb/catalog/database.rb +184 -184
  180. data/lib/rubydb/catalog/index.rb +97 -97
  181. data/lib/rubydb/catalog/schema.rb +103 -103
  182. data/lib/rubydb/catalog/sequence.rb +90 -90
  183. data/lib/rubydb/catalog/system_catalog.rb +698 -698
  184. data/lib/rubydb/catalog/table.rb +178 -178
  185. data/lib/rubydb/catalog/trigger.rb +102 -102
  186. data/lib/rubydb/catalog/view.rb +66 -66
  187. data/lib/rubydb/cli/application.rb +168 -163
  188. data/lib/rubydb/cli/commands/accelerator.rb +72 -0
  189. data/lib/rubydb/cli/commands/backup.rb +80 -81
  190. data/lib/rubydb/cli/commands/branch.rb +72 -72
  191. data/lib/rubydb/cli/commands/checkout.rb +54 -54
  192. data/lib/rubydb/cli/commands/create.rb +58 -58
  193. data/lib/rubydb/cli/commands/diff.rb +76 -77
  194. data/lib/rubydb/cli/commands/doctor.rb +77 -74
  195. data/lib/rubydb/cli/commands/drop.rb +57 -57
  196. data/lib/rubydb/cli/commands/init.rb +101 -102
  197. data/lib/rubydb/cli/commands/inspect.rb +95 -95
  198. data/lib/rubydb/cli/commands/merge.rb +63 -63
  199. data/lib/rubydb/cli/commands/migrate.rb +62 -62
  200. data/lib/rubydb/cli/commands/restart.rb +42 -39
  201. data/lib/rubydb/cli/commands/restore.rb +121 -121
  202. data/lib/rubydb/cli/commands/shell.rb +365 -365
  203. data/lib/rubydb/cli/commands/snapshot.rb +79 -79
  204. data/lib/rubydb/cli/commands/start.rb +88 -82
  205. data/lib/rubydb/cli/commands/status.rb +96 -92
  206. data/lib/rubydb/cli/commands/stop.rb +47 -47
  207. data/lib/rubydb/cli/commands/vacuum.rb +58 -58
  208. data/lib/rubydb/cli/formatter.rb +221 -221
  209. data/lib/rubydb/cli/output.rb +168 -168
  210. data/lib/rubydb/client/client.rb +309 -304
  211. data/lib/rubydb/client/connection.rb +429 -415
  212. data/lib/rubydb/client/connection_pool.rb +168 -168
  213. data/lib/rubydb/client/connection_url.rb +96 -96
  214. data/lib/rubydb/client/prepared_statement.rb +60 -60
  215. data/lib/rubydb/client/result.rb +127 -123
  216. data/lib/rubydb/client/statement.rb +52 -52
  217. data/lib/rubydb/client/transaction.rb +130 -130
  218. data/lib/rubydb/concurrency/concurrency.rb +19 -19
  219. data/lib/rubydb/concurrency/deadlock_detector.rb +148 -150
  220. data/lib/rubydb/concurrency/latch.rb +101 -101
  221. data/lib/rubydb/concurrency/lock_graph.rb +163 -165
  222. data/lib/rubydb/concurrency/mutex.rb +181 -183
  223. data/lib/rubydb/concurrency/rw_lock.rb +180 -180
  224. data/lib/rubydb/concurrency/scheduler.rb +248 -250
  225. data/lib/rubydb/concurrency/worker_pool.rb +145 -143
  226. data/lib/rubydb/configuration/config.rb +170 -170
  227. data/lib/rubydb/configuration/defaults.rb +191 -179
  228. data/lib/rubydb/configuration/environment.rb +152 -152
  229. data/lib/rubydb/configuration/parser.rb +185 -185
  230. data/lib/rubydb/configuration/validation.rb +228 -221
  231. data/lib/rubydb/constants.rb +74 -74
  232. data/lib/rubydb/constraints/check.rb +181 -181
  233. data/lib/rubydb/constraints/constraint.rb +101 -101
  234. data/lib/rubydb/constraints/foreign_key.rb +130 -130
  235. data/lib/rubydb/constraints/not_null.rb +64 -64
  236. data/lib/rubydb/constraints/primary_key.rb +99 -99
  237. data/lib/rubydb/constraints/unique.rb +106 -108
  238. data/lib/rubydb/constraints/validator.rb +349 -350
  239. data/lib/rubydb/errors/authentication_error.rb +10 -10
  240. data/lib/rubydb/errors/authorization_error.rb +23 -23
  241. data/lib/rubydb/errors/client_error.rb +10 -10
  242. data/lib/rubydb/errors/configuration_error.rb +10 -10
  243. data/lib/rubydb/errors/connection_error.rb +10 -10
  244. data/lib/rubydb/errors/constraint_error.rb +23 -23
  245. data/lib/rubydb/errors/corruption_error.rb +10 -10
  246. data/lib/rubydb/errors/database_error.rb +10 -10
  247. data/lib/rubydb/errors/error.rb +20 -20
  248. data/lib/rubydb/errors/execution_error.rb +10 -10
  249. data/lib/rubydb/errors/parser_error.rb +10 -10
  250. data/lib/rubydb/errors/recovery_error.rb +10 -10
  251. data/lib/rubydb/errors/replication_error.rb +10 -10
  252. data/lib/rubydb/errors/server_error.rb +6 -6
  253. data/lib/rubydb/errors/storage_error.rb +10 -10
  254. data/lib/rubydb/errors/transaction_error.rb +10 -10
  255. data/lib/rubydb/execution/accelerator_dispatch.rb +30 -0
  256. data/lib/rubydb/execution/aggregate_executor.rb +134 -138
  257. data/lib/rubydb/execution/cost_model.rb +72 -0
  258. data/lib/rubydb/execution/delete_executor.rb +110 -112
  259. data/lib/rubydb/execution/distinct_executor.rb +131 -135
  260. data/lib/rubydb/execution/executor.rb +1544 -1188
  261. data/lib/rubydb/execution/expression.rb +191 -193
  262. data/lib/rubydb/execution/index_scan.rb +142 -142
  263. data/lib/rubydb/execution/insert_executor.rb +215 -217
  264. data/lib/rubydb/execution/join_executor.rb +243 -249
  265. data/lib/rubydb/execution/limit_executor.rb +83 -85
  266. data/lib/rubydb/execution/operator_selection.rb +57 -0
  267. data/lib/rubydb/execution/optimizer.rb +227 -215
  268. data/lib/rubydb/execution/physical_plan.rb +47 -0
  269. data/lib/rubydb/execution/plan.rb +355 -353
  270. data/lib/rubydb/execution/planner.rb +508 -536
  271. data/lib/rubydb/execution/predicate.rb +235 -235
  272. data/lib/rubydb/execution/scan.rb +49 -49
  273. data/lib/rubydb/execution/sequential_scan.rb +63 -63
  274. data/lib/rubydb/execution/sort_executor.rb +194 -185
  275. data/lib/rubydb/execution/update_executor.rb +160 -162
  276. data/lib/rubydb/functions/aggregate.rb +70 -70
  277. data/lib/rubydb/functions/date_functions.rb +274 -278
  278. data/lib/rubydb/functions/function.rb +85 -85
  279. data/lib/rubydb/functions/json_functions.rb +231 -215
  280. data/lib/rubydb/functions/numeric_functions.rb +346 -346
  281. data/lib/rubydb/functions/scalar.rb +52 -52
  282. data/lib/rubydb/functions/string_functions.rb +383 -383
  283. data/lib/rubydb/functions/system_functions.rb +258 -246
  284. data/lib/rubydb/history/as_of.rb +238 -238
  285. data/lib/rubydb/history/change.rb +105 -105
  286. data/lib/rubydb/history/history.rb +131 -131
  287. data/lib/rubydb/history/history_manager.rb +228 -229
  288. data/lib/rubydb/history/temporal_query.rb +202 -202
  289. data/lib/rubydb/history/timeline.rb +144 -144
  290. data/lib/rubydb/indexes/btree.rb +215 -186
  291. data/lib/rubydb/indexes/btree_cursor.rb +258 -258
  292. data/lib/rubydb/indexes/btree_node.rb +384 -385
  293. data/lib/rubydb/indexes/hash_index.rb +150 -150
  294. data/lib/rubydb/indexes/index.rb +71 -71
  295. data/lib/rubydb/indexes/index_manager.rb +408 -406
  296. data/lib/rubydb/indexes/index_scan.rb +466 -470
  297. data/lib/rubydb/migrations/migration.rb +253 -254
  298. data/lib/rubydb/migrations/migration_lock.rb +146 -146
  299. data/lib/rubydb/migrations/migration_manager.rb +187 -176
  300. data/lib/rubydb/migrations/migration_version.rb +71 -71
  301. data/lib/rubydb/migrations/schema_diff.rb +211 -211
  302. data/lib/rubydb/migrations/schema_version.rb +64 -64
  303. data/lib/rubydb/monitoring/events.rb +155 -160
  304. data/lib/rubydb/monitoring/health.rb +216 -222
  305. data/lib/rubydb/monitoring/logger.rb +188 -193
  306. data/lib/rubydb/monitoring/metrics.rb +363 -359
  307. data/lib/rubydb/monitoring/performance.rb +176 -176
  308. data/lib/rubydb/monitoring/statistics.rb +168 -170
  309. data/lib/rubydb/mvcc/garbage_collector.rb +199 -199
  310. data/lib/rubydb/mvcc/mvcc.rb +16 -16
  311. data/lib/rubydb/mvcc/snapshot.rb +146 -147
  312. data/lib/rubydb/mvcc/vacuum.rb +180 -180
  313. data/lib/rubydb/mvcc/version.rb +106 -106
  314. data/lib/rubydb/mvcc/version_store.rb +396 -398
  315. data/lib/rubydb/mvcc/visibility.rb +107 -109
  316. data/lib/rubydb/protocol/capabilities.rb +125 -125
  317. data/lib/rubydb/protocol/decoder.rb +142 -145
  318. data/lib/rubydb/protocol/encoder.rb +131 -136
  319. data/lib/rubydb/protocol/handshake.rb +306 -305
  320. data/lib/rubydb/protocol/message.rb +121 -121
  321. data/lib/rubydb/protocol/parameter_binder.rb +101 -0
  322. data/lib/rubydb/protocol/protocol.rb +276 -277
  323. data/lib/rubydb/protocol/version.rb +54 -54
  324. data/lib/rubydb/rails/adapter.rb +245 -239
  325. data/lib/rubydb/rails/connection.rb +312 -314
  326. data/lib/rubydb/rails/database_statements.rb +122 -122
  327. data/lib/rubydb/rails/migration.rb +131 -131
  328. data/lib/rubydb/rails/quoting.rb +109 -109
  329. data/lib/rubydb/rails/result.rb +117 -117
  330. data/lib/rubydb/rails/schema_statements.rb +339 -339
  331. data/lib/rubydb/rails/transaction.rb +105 -105
  332. data/lib/rubydb/rails/type.rb +126 -126
  333. data/lib/rubydb/recovery/checkpoint.rb +261 -257
  334. data/lib/rubydb/recovery/consistency.rb +457 -467
  335. data/lib/rubydb/recovery/corruption_detector.rb +5 -5
  336. data/lib/rubydb/recovery/crash_recovery.rb +381 -387
  337. data/lib/rubydb/recovery/recovery_manager.rb +204 -206
  338. data/lib/rubydb/recovery/redo.rb +235 -237
  339. data/lib/rubydb/recovery/undo.rb +204 -206
  340. data/lib/rubydb/replication/failover.rb +5 -5
  341. data/lib/rubydb/replication/fencing.rb +63 -63
  342. data/lib/rubydb/replication/primary.rb +461 -450
  343. data/lib/rubydb/replication/replica.rb +382 -384
  344. data/lib/rubydb/replication/replication_log.rb +194 -200
  345. data/lib/rubydb/replication/replication_manager.rb +307 -308
  346. data/lib/rubydb/replication/replication_slot.rb +293 -295
  347. data/lib/rubydb/replication/replication_stream.rb +198 -201
  348. data/lib/rubydb/rubydb.rb +570 -560
  349. data/lib/rubydb/security/access_control.rb +252 -254
  350. data/lib/rubydb/security/audit_log.rb +209 -213
  351. data/lib/rubydb/security/authentication.rb +302 -302
  352. data/lib/rubydb/security/authorization.rb +282 -282
  353. data/lib/rubydb/security/credentials.rb +192 -196
  354. data/lib/rubydb/security/password.rb +205 -215
  355. data/lib/rubydb/security/permissions.rb +74 -74
  356. data/lib/rubydb/security/role.rb +99 -101
  357. data/lib/rubydb/security/user.rb +86 -86
  358. data/lib/rubydb/server/connection.rb +383 -366
  359. data/lib/rubydb/server/connection_pool.rb +193 -193
  360. data/lib/rubydb/server/lifecycle.rb +227 -228
  361. data/lib/rubydb/server/listener.rb +139 -136
  362. data/lib/rubydb/server/request_handler.rb +277 -276
  363. data/lib/rubydb/server/server.rb +363 -364
  364. data/lib/rubydb/server/session.rb +416 -369
  365. data/lib/rubydb/server/worker.rb +206 -210
  366. data/lib/rubydb/server/worker_pool.rb +168 -168
  367. data/lib/rubydb/sql/ast/alter_table.rb +169 -169
  368. data/lib/rubydb/sql/ast/begin_transaction.rb +47 -47
  369. data/lib/rubydb/sql/ast/commit.rb +37 -37
  370. data/lib/rubydb/sql/ast/constraint.rb +92 -83
  371. data/lib/rubydb/sql/ast/create_database.rb +41 -41
  372. data/lib/rubydb/sql/ast/create_index.rb +61 -61
  373. data/lib/rubydb/sql/ast/create_schema.rb +52 -52
  374. data/lib/rubydb/sql/ast/create_table.rb +187 -187
  375. data/lib/rubydb/sql/ast/delete.rb +54 -54
  376. data/lib/rubydb/sql/ast/drop_database.rb +41 -41
  377. data/lib/rubydb/sql/ast/drop_index.rb +41 -41
  378. data/lib/rubydb/sql/ast/drop_schema.rb +49 -49
  379. data/lib/rubydb/sql/ast/drop_table.rb +49 -49
  380. data/lib/rubydb/sql/ast/explain.rb +64 -64
  381. data/lib/rubydb/sql/ast/expression.rb +617 -604
  382. data/lib/rubydb/sql/ast/insert.rb +66 -66
  383. data/lib/rubydb/sql/ast/node.rb +42 -42
  384. data/lib/rubydb/sql/ast/rollback.rb +63 -63
  385. data/lib/rubydb/sql/ast/savepoint.rb +59 -59
  386. data/lib/rubydb/sql/ast/select.rb +88 -88
  387. data/lib/rubydb/sql/ast/set_operation.rb +22 -20
  388. data/lib/rubydb/sql/ast/trigger.rb +35 -29
  389. data/lib/rubydb/sql/ast/update.rb +88 -88
  390. data/lib/rubydb/sql/ast/vacuum.rb +19 -19
  391. data/lib/rubydb/sql/ast/view.rb +38 -32
  392. data/lib/rubydb/sql/ast/with.rb +32 -32
  393. data/lib/rubydb/sql/grammar.rb +86 -86
  394. data/lib/rubydb/sql/keywords.rb +156 -156
  395. data/lib/rubydb/sql/lexer.rb +209 -214
  396. data/lib/rubydb/sql/operators.rb +100 -100
  397. data/lib/rubydb/sql/parser.rb +1167 -1170
  398. data/lib/rubydb/sql/planner/analyzer.rb +283 -302
  399. data/lib/rubydb/sql/planner/binder.rb +537 -550
  400. data/lib/rubydb/sql/planner/type_checker.rb +427 -431
  401. data/lib/rubydb/sql/token.rb +210 -210
  402. data/lib/rubydb/storage/buffer_frame.rb +44 -44
  403. data/lib/rubydb/storage/buffer_pool.rb +155 -155
  404. data/lib/rubydb/storage/database_lock.rb +74 -74
  405. data/lib/rubydb/storage/deserializer.rb +332 -342
  406. data/lib/rubydb/storage/engine.rb +2409 -2330
  407. data/lib/rubydb/storage/file_manager.rb +191 -187
  408. data/lib/rubydb/storage/free_space_map.rb +79 -81
  409. data/lib/rubydb/storage/page.rb +92 -94
  410. data/lib/rubydb/storage/page_allocator.rb +852 -855
  411. data/lib/rubydb/storage/page_header.rb +63 -67
  412. data/lib/rubydb/storage/page_manager.rb +127 -131
  413. data/lib/rubydb/storage/record.rb +58 -58
  414. data/lib/rubydb/storage/row.rb +78 -78
  415. data/lib/rubydb/storage/serializer.rb +51 -51
  416. data/lib/rubydb/storage/snapshot_reader.rb +167 -0
  417. data/lib/rubydb/storage/storage_layout.rb +151 -151
  418. data/lib/rubydb/storage/storage_manager.rb +114 -114
  419. data/lib/rubydb/storage/tuple.rb +458 -461
  420. data/lib/rubydb/storage/visibility_map.rb +964 -973
  421. data/lib/rubydb/transactions/commit_manager.rb +219 -220
  422. data/lib/rubydb/transactions/isolation.rb +98 -98
  423. data/lib/rubydb/transactions/lock.rb +76 -76
  424. data/lib/rubydb/transactions/lock_manager.rb +359 -362
  425. data/lib/rubydb/transactions/savepoint.rb +142 -143
  426. data/lib/rubydb/transactions/transaction.rb +214 -215
  427. data/lib/rubydb/transactions/transaction_id.rb +84 -84
  428. data/lib/rubydb/transactions/transaction_log.rb +256 -257
  429. data/lib/rubydb/transactions/transaction_manager.rb +434 -435
  430. data/lib/rubydb/types/bigint.rb +36 -36
  431. data/lib/rubydb/types/blob.rb +37 -37
  432. data/lib/rubydb/types/boolean.rb +34 -34
  433. data/lib/rubydb/types/date.rb +39 -39
  434. data/lib/rubydb/types/decimal.rb +48 -48
  435. data/lib/rubydb/types/float.rb +34 -34
  436. data/lib/rubydb/types/integer.rb +36 -36
  437. data/lib/rubydb/types/json.rb +41 -41
  438. data/lib/rubydb/types/null.rb +34 -34
  439. data/lib/rubydb/types/smallint.rb +36 -36
  440. data/lib/rubydb/types/text.rb +37 -37
  441. data/lib/rubydb/types/time.rb +46 -46
  442. data/lib/rubydb/types/timestamp.rb +39 -39
  443. data/lib/rubydb/types/type.rb +119 -119
  444. data/lib/rubydb/types/uuid.rb +47 -47
  445. data/lib/rubydb/types/varchar.rb +37 -37
  446. data/lib/rubydb/version.rb +32 -32
  447. data/lib/rubydb/wal/archive.rb +207 -193
  448. data/lib/rubydb/wal/checkpoint.rb +181 -183
  449. data/lib/rubydb/wal/lsn.rb +94 -94
  450. data/lib/rubydb/wal/reader.rb +259 -260
  451. data/lib/rubydb/wal/record.rb +105 -105
  452. data/lib/rubydb/wal/segment.rb +193 -193
  453. data/lib/rubydb/wal/wal.rb +481 -452
  454. data/lib/rubydb/wal/writer.rb +236 -236
  455. data/lib/rubydb.rb +7 -7
  456. data/packaging/docker/docker-compose.failover.yml +43 -43
  457. data/packaging/homebrew/rubydb.rb +19 -19
  458. data/rubydb.gemspec +70 -57
  459. data/scripts/benchmark +7 -7
  460. data/scripts/build_accelerator +49 -0
  461. data/scripts/durability_drill +37 -37
  462. data/scripts/fuzz +63 -63
  463. data/scripts/release +77 -42
  464. data/scripts/release_check +43 -43
  465. data/scripts/replication_failover_drill +268 -250
  466. data/scripts/replication_network_failover_drill +287 -255
  467. data/scripts/restore_drill +45 -45
  468. data/scripts/security +45 -0
  469. metadata +102 -1
@@ -1,214 +1,209 @@
1
- # frozen_string_literal: true
2
-
3
- require "strscan"
4
- require_relative "../errors/error"
5
- require_relative "../errors/parser_error"
6
-
7
- module RubyDB
8
- module SQL
9
- # SQL Lexer - converts SQL text into tokens
10
- class Lexer
11
- attr_reader :tokens, :current_position
12
-
13
- def initialize(text)
14
- @text = text
15
- @scanner = StringScanner.new(text)
16
- @tokens = []
17
- @current_position = { line: 1, column: 1 }
18
- @line_start = 0
19
- @current_position[:line] = 1
20
- @current_position[:column] = 1
21
- end
22
-
23
- def tokenize
24
- @tokens = []
25
- until @scanner.eos?
26
- skip_whitespace
27
- break if @scanner.eos?
28
-
29
- token = scan_token
30
- @tokens << token if token
31
- end
32
- @tokens << Token.new(Token::Type::EOF, line: @current_position[:line], column: @current_position[:column])
33
- @tokens
34
- end
35
-
36
- private
37
-
38
- def skip_whitespace
39
- while true
40
- case
41
- when @scanner.scan(/\s+/)
42
- update_position($&)
43
- when @scanner.scan(/--[^\n]*/)
44
- update_position($&)
45
- next
46
- when @scanner.scan(/\/\*.*?\*\//m)
47
- update_position($&)
48
- next
49
- else
50
- break
51
- end
52
- end
53
- end
54
-
55
- def scan_token
56
- pos = { line: @current_position[:line], column: @current_position[:column] }
57
-
58
- token =
59
- case
60
- when @scanner.scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
61
- word = @scanner.matched.upcase
62
- update_position($&)
63
- if Keywords.keyword?(word)
64
- Token.new(Keywords.token_type(word), word, **pos)
65
- else
66
- Token.new(Token::Type::IDENTIFIER, @scanner.matched, **pos)
67
- end
68
-
69
- when @scanner.scan(/'(?:''|[^'])*'/)
70
- update_position($&)
71
- Token.new(Token::Type::STRING, @scanner.matched[1..-2].gsub("''", "'"), **pos)
72
-
73
- when @scanner.scan(/"[^"]*"/)
74
- update_position($&)
75
- Token.new(Token::Type::IDENTIFIER, @scanner.matched[1..-2], **pos)
76
-
77
- when @scanner.scan(/`[^`]*`/)
78
- update_position($&)
79
- Token.new(Token::Type::IDENTIFIER, @scanner.matched[1..-2], **pos)
80
-
81
- when @scanner.scan(/\d+\.\d+/)
82
- update_position($&)
83
- Token.new(Token::Type::NUMBER, @scanner.matched.to_f, **pos)
84
-
85
- when @scanner.scan(/\d+/)
86
- update_position($&)
87
- Token.new(Token::Type::NUMBER, @scanner.matched.to_i, **pos)
88
-
89
- when @scanner.scan(/\?/)
90
- update_position($&)
91
- Token.new(Token::Type::PARAMETER, nil, **pos)
92
-
93
- when @scanner.scan(/\$\d+/)
94
- update_position($&)
95
- Token.new(Token::Type::PARAMETER, @scanner.matched[1..-1].to_i, **pos)
96
-
97
- when @scanner.scan(/<=/)
98
- update_position($&)
99
- Token.new(Token::Type::LTE, "<=", **pos)
100
-
101
- when @scanner.scan(/>=/)
102
- update_position($&)
103
- Token.new(Token::Type::GTE, ">=", **pos)
104
-
105
- when @scanner.scan(/!=|<>/)
106
- update_position($&)
107
- Token.new(Token::Type::NE, @scanner.matched, **pos)
108
-
109
- when @scanner.scan(/=/)
110
- update_position($&)
111
- Token.new(Token::Type::EQ, "=", **pos)
112
-
113
- when @scanner.scan(/</)
114
- update_position($&)
115
- Token.new(Token::Type::LT, "<", **pos)
116
-
117
- when @scanner.scan(/>/)
118
- update_position($&)
119
- Token.new(Token::Type::GT, ">", **pos)
120
-
121
- when @scanner.scan(/\+/)
122
- update_position($&)
123
- Token.new(Token::Type::PLUS, "+", **pos)
124
-
125
- when @scanner.scan(/-/)
126
- update_position($&)
127
- Token.new(Token::Type::MINUS, "-", **pos)
128
-
129
- when @scanner.scan(/\*/)
130
- update_position($&)
131
- Token.new(Token::Type::STAR, "*", **pos)
132
-
133
- when @scanner.scan(/\//)
134
- update_position($&)
135
- Token.new(Token::Type::SLASH, "/", **pos)
136
-
137
- when @scanner.scan(/\%/)
138
- update_position($&)
139
- Token.new(Token::Type::PERCENT, "%", **pos)
140
-
141
- when @scanner.scan(/\(/)
142
- update_position($&)
143
- Token.new(Token::Type::LPAREN, "(", **pos)
144
-
145
- when @scanner.scan(/\)/)
146
- update_position($&)
147
- Token.new(Token::Type::RPAREN, ")", **pos)
148
-
149
- when @scanner.scan(/,/)
150
- update_position($&)
151
- Token.new(Token::Type::COMMA, ",", **pos)
152
-
153
- when @scanner.scan(/;/)
154
- update_position($&)
155
- Token.new(Token::Type::SEMICOLON, ";", **pos)
156
-
157
- when @scanner.scan(/\./)
158
- update_position($&)
159
- Token.new(Token::Type::DOT, ".", **pos)
160
-
161
- when @scanner.scan(/\[/)
162
- update_position($&)
163
- Token.new(Token::Type::LBRACKET, "[", **pos)
164
-
165
- when @scanner.scan(/\]/)
166
- update_position($&)
167
- Token.new(Token::Type::RBRACKET, "]", **pos)
168
-
169
- when @scanner.scan(/\{/)
170
- update_position($&)
171
- Token.new(Token::Type::LBRACE, "{", **pos)
172
-
173
- when @scanner.scan(/\}/)
174
- update_position($&)
175
- Token.new(Token::Type::RBRACE, "}", **pos)
176
-
177
- when @scanner.scan(/&/)
178
- update_position($&)
179
- Token.new(Token::Type::AMPERSAND, "&", **pos)
180
-
181
- when @scanner.scan(/\|/)
182
- update_position($&)
183
- Token.new(Token::Type::PIPE, "|", **pos)
184
-
185
- when @scanner.scan(/\^/)
186
- update_position($&)
187
- Token.new(Token::Type::CARET, "^", **pos)
188
-
189
- when @scanner.scan(/~/)
190
- update_position($&)
191
- Token.new(Token::Type::TILDE, "~", **pos)
192
-
193
- else
194
- char = @scanner.getch
195
- update_position(char)
196
- raise RubyDB::ParserError, "Unexpected character #{char.inspect} at #{@current_position[:line]}:#{@current_position[:column]}"
197
- end
198
-
199
- token
200
- end
201
-
202
- def update_position(text)
203
- text ||= @scanner.matched
204
- lines = text.count("\n")
205
- if lines > 0
206
- @current_position[:line] += lines
207
- @current_position[:column] = text.length - text.rindex("\n")
208
- else
209
- @current_position[:column] += text.length
210
- end
211
- end
212
- end
213
- end
214
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+ require_relative "../errors/error"
5
+ require_relative "../errors/parser_error"
6
+
7
+ module RubyDB
8
+ module SQL
9
+ # SQL Lexer - converts SQL text into tokens
10
+ class Lexer
11
+ attr_reader :tokens, :current_position
12
+
13
+ def initialize(text)
14
+ @text = text
15
+ @scanner = StringScanner.new(text)
16
+ @tokens = []
17
+ @current_position = {line: 1, column: 1}
18
+ @line_start = 0
19
+ @current_position[:line] = 1
20
+ @current_position[:column] = 1
21
+ end
22
+
23
+ def tokenize
24
+ @tokens = []
25
+ until @scanner.eos?
26
+ skip_whitespace
27
+ break if @scanner.eos?
28
+
29
+ token = scan_token
30
+ @tokens << token if token
31
+ end
32
+ @tokens << Token.new(Token::Type::EOF, line: @current_position[:line], column: @current_position[:column])
33
+ @tokens
34
+ end
35
+
36
+ private
37
+
38
+ def skip_whitespace
39
+ loop do
40
+ if @scanner.scan(/\s+/)
41
+ update_position($&)
42
+ elsif @scanner.scan(/--[^\n]*/)
43
+ update_position($&)
44
+ next
45
+ elsif @scanner.scan(/\/\*.*?\*\//m)
46
+ update_position($&)
47
+ next
48
+ else
49
+ break
50
+ end
51
+ end
52
+ end
53
+
54
+ def scan_token
55
+ pos = {line: @current_position[:line], column: @current_position[:column]}
56
+
57
+ if @scanner.scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
58
+ word = @scanner.matched.upcase
59
+ update_position($&)
60
+ if Keywords.keyword?(word)
61
+ Token.new(Keywords.token_type(word), word, **pos)
62
+ else
63
+ Token.new(Token::Type::IDENTIFIER, @scanner.matched, **pos)
64
+ end
65
+
66
+ elsif @scanner.scan(/'(?:''|[^'])*'/)
67
+ update_position($&)
68
+ Token.new(Token::Type::STRING, @scanner.matched[1..-2].gsub("''", "'"), **pos)
69
+
70
+ elsif @scanner.scan(/"[^"]*"/)
71
+ update_position($&)
72
+ Token.new(Token::Type::IDENTIFIER, @scanner.matched[1..-2], **pos)
73
+
74
+ elsif @scanner.scan(/`[^`]*`/)
75
+ update_position($&)
76
+ Token.new(Token::Type::IDENTIFIER, @scanner.matched[1..-2], **pos)
77
+
78
+ elsif @scanner.scan(/\d+\.\d+/)
79
+ update_position($&)
80
+ Token.new(Token::Type::NUMBER, @scanner.matched.to_f, **pos)
81
+
82
+ elsif @scanner.scan(/\d+/)
83
+ update_position($&)
84
+ Token.new(Token::Type::NUMBER, @scanner.matched.to_i, **pos)
85
+
86
+ elsif @scanner.scan("?")
87
+ update_position($&)
88
+ Token.new(Token::Type::PARAMETER, nil, **pos)
89
+
90
+ elsif @scanner.scan(/\$\d+/)
91
+ update_position($&)
92
+ Token.new(Token::Type::PARAMETER, @scanner.matched[1..].to_i, **pos)
93
+
94
+ elsif @scanner.scan("<=")
95
+ update_position($&)
96
+ Token.new(Token::Type::LTE, "<=", **pos)
97
+
98
+ elsif @scanner.scan(">=")
99
+ update_position($&)
100
+ Token.new(Token::Type::GTE, ">=", **pos)
101
+
102
+ elsif @scanner.scan(/!=|<>/)
103
+ update_position($&)
104
+ Token.new(Token::Type::NE, @scanner.matched, **pos)
105
+
106
+ elsif @scanner.scan("=")
107
+ update_position($&)
108
+ Token.new(Token::Type::EQ, "=", **pos)
109
+
110
+ elsif @scanner.scan("<")
111
+ update_position($&)
112
+ Token.new(Token::Type::LT, "<", **pos)
113
+
114
+ elsif @scanner.scan(">")
115
+ update_position($&)
116
+ Token.new(Token::Type::GT, ">", **pos)
117
+
118
+ elsif @scanner.scan("+")
119
+ update_position($&)
120
+ Token.new(Token::Type::PLUS, "+", **pos)
121
+
122
+ elsif @scanner.scan("-")
123
+ update_position($&)
124
+ Token.new(Token::Type::MINUS, "-", **pos)
125
+
126
+ elsif @scanner.scan("*")
127
+ update_position($&)
128
+ Token.new(Token::Type::STAR, "*", **pos)
129
+
130
+ elsif @scanner.scan("/")
131
+ update_position($&)
132
+ Token.new(Token::Type::SLASH, "/", **pos)
133
+
134
+ elsif @scanner.scan("%")
135
+ update_position($&)
136
+ Token.new(Token::Type::PERCENT, "%", **pos)
137
+
138
+ elsif @scanner.scan("(")
139
+ update_position($&)
140
+ Token.new(Token::Type::LPAREN, "(", **pos)
141
+
142
+ elsif @scanner.scan(")")
143
+ update_position($&)
144
+ Token.new(Token::Type::RPAREN, ")", **pos)
145
+
146
+ elsif @scanner.scan(",")
147
+ update_position($&)
148
+ Token.new(Token::Type::COMMA, ",", **pos)
149
+
150
+ elsif @scanner.scan(";")
151
+ update_position($&)
152
+ Token.new(Token::Type::SEMICOLON, ";", **pos)
153
+
154
+ elsif @scanner.scan(".")
155
+ update_position($&)
156
+ Token.new(Token::Type::DOT, ".", **pos)
157
+
158
+ elsif @scanner.scan("[")
159
+ update_position($&)
160
+ Token.new(Token::Type::LBRACKET, "[", **pos)
161
+
162
+ elsif @scanner.scan("]")
163
+ update_position($&)
164
+ Token.new(Token::Type::RBRACKET, "]", **pos)
165
+
166
+ elsif @scanner.scan("{")
167
+ update_position($&)
168
+ Token.new(Token::Type::LBRACE, "{", **pos)
169
+
170
+ elsif @scanner.scan("}")
171
+ update_position($&)
172
+ Token.new(Token::Type::RBRACE, "}", **pos)
173
+
174
+ elsif @scanner.scan("&")
175
+ update_position($&)
176
+ Token.new(Token::Type::AMPERSAND, "&", **pos)
177
+
178
+ elsif @scanner.scan("|")
179
+ update_position($&)
180
+ Token.new(Token::Type::PIPE, "|", **pos)
181
+
182
+ elsif @scanner.scan("^")
183
+ update_position($&)
184
+ Token.new(Token::Type::CARET, "^", **pos)
185
+
186
+ elsif @scanner.scan("~")
187
+ update_position($&)
188
+ Token.new(Token::Type::TILDE, "~", **pos)
189
+
190
+ else
191
+ char = @scanner.getch
192
+ update_position(char)
193
+ raise RubyDB::ParserError, "Unexpected character #{char.inspect} at #{@current_position[:line]}:#{@current_position[:column]}"
194
+ end
195
+ end
196
+
197
+ def update_position(text)
198
+ text ||= @scanner.matched
199
+ lines = text.count("\n")
200
+ if lines > 0
201
+ @current_position[:line] += lines
202
+ @current_position[:column] = text.length - text.rindex("\n")
203
+ else
204
+ @current_position[:column] += text.length
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
@@ -1,100 +1,100 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyDB
4
- module SQL
5
- # SQL operator definitions
6
- module Operators
7
- # Operator precedence (higher = binds tighter)
8
- PRECEDENCE = {
9
- Token::Type::DOT => 18,
10
- Token::Type::LBRACKET => 18,
11
- Token::Type::TILDE => 17,
12
- Token::Type::STAR => 16,
13
- Token::Type::SLASH => 16,
14
- Token::Type::PERCENT => 16,
15
- Token::Type::PLUS => 15,
16
- Token::Type::MINUS => 15,
17
- Token::Type::AMPERSAND => 14,
18
- Token::Type::PIPE => 13,
19
- Token::Type::CARET => 12,
20
- Token::Type::LT => 11,
21
- Token::Type::LTE => 11,
22
- Token::Type::GT => 11,
23
- Token::Type::GTE => 11,
24
- Token::Type::EQ => 10,
25
- Token::Type::NE => 10,
26
- Token::Type::LIKE => 9,
27
- Token::Type::ILIKE => 9,
28
- Token::Type::NOT => 8,
29
- Token::Type::AND => 7,
30
- Token::Type::OR => 6,
31
- Token::Type::BETWEEN => 5,
32
- Token::Type::IN => 5,
33
- Token::Type::IS => 5
34
- }.freeze
35
-
36
- # Operator associativity: :left, :right, or :nonassoc
37
- ASSOCIATIVITY = {
38
- Token::Type::DOT => :left,
39
- Token::Type::LBRACKET => :left,
40
- Token::Type::TILDE => :right,
41
- Token::Type::STAR => :left,
42
- Token::Type::SLASH => :left,
43
- Token::Type::PERCENT => :left,
44
- Token::Type::PLUS => :left,
45
- Token::Type::MINUS => :left,
46
- Token::Type::AMPERSAND => :left,
47
- Token::Type::PIPE => :left,
48
- Token::Type::CARET => :left,
49
- Token::Type::LT => :nonassoc,
50
- Token::Type::LTE => :nonassoc,
51
- Token::Type::GT => :nonassoc,
52
- Token::Type::GTE => :nonassoc,
53
- Token::Type::EQ => :nonassoc,
54
- Token::Type::NE => :nonassoc,
55
- Token::Type::LIKE => :nonassoc,
56
- Token::Type::ILIKE => :nonassoc,
57
- Token::Type::NOT => :right,
58
- Token::Type::AND => :left,
59
- Token::Type::OR => :left,
60
- Token::Type::BETWEEN => :nonassoc,
61
- Token::Type::IN => :nonassoc,
62
- Token::Type::IS => :nonassoc
63
- }.freeze
64
-
65
- def self.precedence(token_type)
66
- PRECEDENCE[token_type] || 0
67
- end
68
-
69
- def self.associativity(token_type)
70
- ASSOCIATIVITY[token_type] || :left
71
- end
72
-
73
- def self.comparison?(token_type)
74
- [Token::Type::EQ, Token::Type::NE, Token::Type::LT,
75
- Token::Type::LTE, Token::Type::GT, Token::Type::GTE,
76
- Token::Type::LIKE, Token::Type::ILIKE, Token::Type::BETWEEN,
77
- Token::Type::IN, Token::Type::IS].include?(token_type)
78
- end
79
-
80
- def self.logical?(token_type)
81
- [Token::Type::AND, Token::Type::OR, Token::Type::NOT].include?(token_type)
82
- end
83
-
84
- def self.arithmetic?(token_type)
85
- [Token::Type::PLUS, Token::Type::MINUS, Token::Type::STAR,
86
- Token::Type::SLASH, Token::Type::PERCENT].include?(token_type)
87
- end
88
-
89
- def self.bitwise?(token_type)
90
- [Token::Type::AMPERSAND, Token::Type::PIPE,
91
- Token::Type::CARET, Token::Type::TILDE].include?(token_type)
92
- end
93
-
94
- def self.unary?(token_type)
95
- [Token::Type::PLUS, Token::Type::MINUS, Token::Type::TILDE,
96
- Token::Type::NOT].include?(token_type)
97
- end
98
- end
99
- end
100
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module SQL
5
+ # SQL operator definitions
6
+ module Operators
7
+ # Operator precedence (higher = binds tighter)
8
+ PRECEDENCE = {
9
+ Token::Type::DOT => 18,
10
+ Token::Type::LBRACKET => 18,
11
+ Token::Type::TILDE => 17,
12
+ Token::Type::STAR => 16,
13
+ Token::Type::SLASH => 16,
14
+ Token::Type::PERCENT => 16,
15
+ Token::Type::PLUS => 15,
16
+ Token::Type::MINUS => 15,
17
+ Token::Type::AMPERSAND => 14,
18
+ Token::Type::PIPE => 13,
19
+ Token::Type::CARET => 12,
20
+ Token::Type::LT => 11,
21
+ Token::Type::LTE => 11,
22
+ Token::Type::GT => 11,
23
+ Token::Type::GTE => 11,
24
+ Token::Type::EQ => 10,
25
+ Token::Type::NE => 10,
26
+ Token::Type::LIKE => 9,
27
+ Token::Type::ILIKE => 9,
28
+ Token::Type::NOT => 8,
29
+ Token::Type::AND => 7,
30
+ Token::Type::OR => 6,
31
+ Token::Type::BETWEEN => 5,
32
+ Token::Type::IN => 5,
33
+ Token::Type::IS => 5
34
+ }.freeze
35
+
36
+ # Operator associativity: :left, :right, or :nonassoc
37
+ ASSOCIATIVITY = {
38
+ Token::Type::DOT => :left,
39
+ Token::Type::LBRACKET => :left,
40
+ Token::Type::TILDE => :right,
41
+ Token::Type::STAR => :left,
42
+ Token::Type::SLASH => :left,
43
+ Token::Type::PERCENT => :left,
44
+ Token::Type::PLUS => :left,
45
+ Token::Type::MINUS => :left,
46
+ Token::Type::AMPERSAND => :left,
47
+ Token::Type::PIPE => :left,
48
+ Token::Type::CARET => :left,
49
+ Token::Type::LT => :nonassoc,
50
+ Token::Type::LTE => :nonassoc,
51
+ Token::Type::GT => :nonassoc,
52
+ Token::Type::GTE => :nonassoc,
53
+ Token::Type::EQ => :nonassoc,
54
+ Token::Type::NE => :nonassoc,
55
+ Token::Type::LIKE => :nonassoc,
56
+ Token::Type::ILIKE => :nonassoc,
57
+ Token::Type::NOT => :right,
58
+ Token::Type::AND => :left,
59
+ Token::Type::OR => :left,
60
+ Token::Type::BETWEEN => :nonassoc,
61
+ Token::Type::IN => :nonassoc,
62
+ Token::Type::IS => :nonassoc
63
+ }.freeze
64
+
65
+ def self.precedence(token_type)
66
+ PRECEDENCE[token_type] || 0
67
+ end
68
+
69
+ def self.associativity(token_type)
70
+ ASSOCIATIVITY[token_type] || :left
71
+ end
72
+
73
+ def self.comparison?(token_type)
74
+ [Token::Type::EQ, Token::Type::NE, Token::Type::LT,
75
+ Token::Type::LTE, Token::Type::GT, Token::Type::GTE,
76
+ Token::Type::LIKE, Token::Type::ILIKE, Token::Type::BETWEEN,
77
+ Token::Type::IN, Token::Type::IS].include?(token_type)
78
+ end
79
+
80
+ def self.logical?(token_type)
81
+ [Token::Type::AND, Token::Type::OR, Token::Type::NOT].include?(token_type)
82
+ end
83
+
84
+ def self.arithmetic?(token_type)
85
+ [Token::Type::PLUS, Token::Type::MINUS, Token::Type::STAR,
86
+ Token::Type::SLASH, Token::Type::PERCENT].include?(token_type)
87
+ end
88
+
89
+ def self.bitwise?(token_type)
90
+ [Token::Type::AMPERSAND, Token::Type::PIPE,
91
+ Token::Type::CARET, Token::Type::TILDE].include?(token_type)
92
+ end
93
+
94
+ def self.unary?(token_type)
95
+ [Token::Type::PLUS, Token::Type::MINUS, Token::Type::TILDE,
96
+ Token::Type::NOT].include?(token_type)
97
+ end
98
+ end
99
+ end
100
+ end