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.
- checksums.yaml +4 -4
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -15
- data/.github/workflows/benchmark.yml +26 -26
- data/.github/workflows/compatibility.yml +63 -63
- data/.github/workflows/fuzz.yml +33 -33
- data/.github/workflows/lint.yml +21 -21
- data/.github/workflows/operations.yml +24 -24
- data/.github/workflows/production-validation.yml +111 -111
- data/.github/workflows/release.yml +77 -77
- data/.github/workflows/security.yml +39 -37
- data/.github/workflows/test.yml +26 -26
- data/.github/workflows/workload.yml +58 -58
- data/.gitignore +16 -5
- data/.rubocop.yml +50 -44
- data/.standard.yml +9 -14
- data/ARCHITECTURE.md +21 -21
- data/CHANGELOG.md +57 -27
- data/CODE_OF_CONDUCT.md +13 -13
- data/CONTRIBUTING.md +29 -29
- data/GOVERNANCE.md +16 -16
- data/Gemfile +18 -17
- data/Gemfile.lock +125 -71
- data/README.md +168 -12
- data/ROADMAP.md +27 -27
- data/Rakefile +76 -71
- data/SECURITY.md +54 -54
- data/SUPPORT.md +14 -14
- data/accelerator/bin/SHA256SUMS +6 -0
- data/accelerator/bin/rubydb-accelerator-darwin-amd64 +0 -0
- data/accelerator/bin/rubydb-accelerator-darwin-arm64 +0 -0
- data/accelerator/bin/rubydb-accelerator-linux-amd64 +0 -0
- data/accelerator/bin/rubydb-accelerator-linux-arm64 +0 -0
- data/accelerator/bin/rubydb-accelerator-windows-amd64.exe +0 -0
- data/accelerator/bin/rubydb-accelerator-windows-arm64.exe +0 -0
- data/accelerator/cmd/rubydb-accelerator/main.go +11 -0
- data/accelerator/go.mod +3 -0
- data/accelerator/internal/execution/aggregate.go +94 -0
- data/accelerator/internal/execution/distinct.go +22 -0
- data/accelerator/internal/execution/filter.go +73 -0
- data/accelerator/internal/execution/join.go +79 -0
- data/accelerator/internal/execution/operators.go +167 -0
- data/accelerator/internal/execution/scan.go +20 -0
- data/accelerator/internal/execution/sort.go +62 -0
- data/accelerator/internal/execution/types.go +136 -0
- data/accelerator/internal/execution/value.go +67 -0
- data/accelerator/internal/memory/arena.go +47 -0
- data/accelerator/internal/memory/reuse.go +22 -0
- data/accelerator/internal/metrics/registry.go +67 -0
- data/accelerator/internal/parallel/bounded_queue.go +56 -0
- data/accelerator/internal/parallel/scheduler.go +47 -0
- data/accelerator/internal/parallel/worker_pool.go +53 -0
- data/accelerator/internal/protocol/cancellation.go +48 -0
- data/accelerator/internal/protocol/columnar.go +263 -0
- data/accelerator/internal/protocol/frame.go +187 -0
- data/accelerator/internal/runtime/worker.go +521 -0
- data/accelerator/internal/storage/page_reader.go +81 -0
- data/accelerator/internal/storage/snapshot_scan.go +539 -0
- data/accelerator/internal/wal/checksum.go +13 -0
- data/accelerator/internal/wal/compression.go +41 -0
- data/accelerator/internal/wal/group_commit.go +24 -0
- data/accelerator/internal/wal/record_encoder.go +40 -0
- data/adapters/activerecord/Gemfile +11 -11
- data/adapters/activerecord/README.md +8 -3
- data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +881 -879
- data/adapters/activerecord/rubydb-activerecord.gemspec +21 -21
- data/adapters/activerecord/spec/rubydb_adapter_integration_spec.rb +143 -143
- data/adapters/ruby/README.md +18 -18
- data/adapters/sequel/README.md +11 -11
- data/config/monitoring/prometheus-alerts.yml +39 -39
- data/config/production.yml +36 -36
- data/docs/README.md +77 -71
- data/docs/architecture/concurrency.md +14 -14
- data/docs/architecture/current-state.md +125 -125
- data/docs/architecture/execution-engine.md +25 -25
- data/docs/architecture/go-accelerator.md +179 -0
- data/docs/architecture/indexes.md +19 -19
- data/docs/architecture/mvcc.md +19 -19
- data/docs/architecture/overview.md +13 -13
- data/docs/architecture/pages.md +11 -11
- data/docs/architecture/production-roadmap.md +82 -82
- data/docs/architecture/query-planner.md +20 -20
- data/docs/architecture/recovery.md +18 -18
- data/docs/architecture/sql-engine.md +12 -12
- data/docs/architecture/storage-engine.md +14 -14
- data/docs/architecture/transactions.md +10 -10
- data/docs/architecture/wal.md +28 -28
- data/docs/cli-cheatsheet.md +98 -98
- data/docs/cli.md +299 -275
- data/docs/contributing/architecture.md +9 -9
- data/docs/contributing/benchmarking.md +30 -14
- data/docs/contributing/development.md +16 -16
- data/docs/contributing/release-process.md +49 -49
- data/docs/contributing/testing.md +16 -16
- data/docs/debugging.md +229 -229
- data/docs/developer/branching.md +10 -10
- data/docs/developer/database-diff.md +10 -10
- data/docs/developer/local-development.md +49 -17
- data/docs/developer/snapshots.md +9 -9
- data/docs/developer/temporal-data.md +10 -10
- data/docs/developer-guide.md +297 -297
- data/docs/getting-started/first-database.md +16 -16
- data/docs/getting-started/first-query.md +13 -13
- data/docs/getting-started/installation.md +19 -19
- data/docs/getting-started/local-to-production.md +300 -300
- data/docs/getting-started/quickstart.md +17 -17
- data/docs/getting-started/rails.md +16 -16
- data/docs/hardening_backlog.md +93 -93
- data/docs/lessons-learned.md +112 -112
- data/docs/operations/backups.md +33 -33
- data/docs/operations/disaster-recovery.md +31 -31
- data/docs/operations/failover.md +30 -30
- data/docs/operations/monitoring.md +25 -25
- data/docs/operations/production-guide.md +295 -295
- data/docs/operations/production-runbook.md +45 -45
- data/docs/operations/replication.md +33 -33
- data/docs/operations/restore.md +6 -6
- data/docs/operations/runbook.md +34 -34
- data/docs/operations/upgrades.md +14 -14
- data/docs/operations/workload-testing.md +17 -17
- data/docs/production-readiness.md +118 -118
- data/docs/production_validation.md +150 -150
- data/docs/rails/active-record.md +11 -11
- data/docs/rails/compatibility-guide.md +90 -90
- data/docs/rails/database-yml.md +92 -92
- data/docs/rails/installation.md +17 -17
- data/docs/rails/migrations.md +17 -17
- data/docs/rails/production.md +82 -82
- data/docs/rails/troubleshooting.md +18 -18
- data/docs/release.md +25 -25
- data/docs/server/architecture.md +10 -10
- data/docs/server/authentication.md +10 -10
- data/docs/server/configuration.md +16 -16
- data/docs/server/connection-pooling.md +10 -10
- data/docs/server/deployment.md +10 -10
- data/docs/server/protocol.md +12 -12
- data/docs/sql/compatibility-guide.md +82 -82
- data/docs/sql/compatibility.md +39 -39
- data/docs/sql/data-types.md +10 -10
- data/docs/sql/functions.md +9 -9
- data/docs/sql/joins.md +9 -9
- data/docs/sql/operators.md +9 -9
- data/docs/sql/sqlite-compatibility.md +21 -21
- data/docs/sql/syntax.md +10 -10
- data/docs/sql/transactions.md +10 -10
- data/docs/troubleshooting.md +244 -244
- data/lessons/01-foundations.md +73 -0
- data/lessons/02-local-development.md +121 -0
- data/lessons/03-embedded-rubydb.md +99 -0
- data/lessons/04-rails-complex-apps.md +138 -0
- data/lessons/05-rubydb-production-server.md +237 -0
- data/lessons/06-postgresql-massive-apps.md +96 -0
- data/lessons/07-hybrid-microservices.md +179 -0
- data/lessons/08-migrations-backups-recovery.md +86 -0
- data/lessons/09-observability-security-scale.md +87 -0
- data/lessons/10-release-readiness.md +192 -0
- data/lessons/11-community-adapter.md +323 -0
- data/lessons/12-rails-ecommerce-pressure.md +263 -0
- data/lib/rubydb/accelerator/client.rb +451 -0
- data/lib/rubydb/accelerator/error.rb +22 -0
- data/lib/rubydb/accelerator/manager.rb +606 -0
- data/lib/rubydb/accelerator.rb +13 -0
- data/lib/rubydb/backup/archive.rb +332 -334
- data/lib/rubydb/backup/backup.rb +400 -401
- data/lib/rubydb/backup/incremental.rb +349 -353
- data/lib/rubydb/backup/restore.rb +289 -290
- data/lib/rubydb/backup/snapshot.rb +265 -267
- data/lib/rubydb/backup/verification.rb +276 -279
- data/lib/rubydb/branching/branch.rb +181 -181
- data/lib/rubydb/branching/branch_manager.rb +307 -311
- data/lib/rubydb/branching/branch_metadata.rb +140 -140
- data/lib/rubydb/branching/checkout.rb +165 -166
- data/lib/rubydb/branching/copy_on_write.rb +272 -272
- data/lib/rubydb/branching/diff.rb +137 -138
- data/lib/rubydb/branching/merge.rb +282 -285
- data/lib/rubydb/build_info.rb +15 -15
- data/lib/rubydb/catalog/catalog.rb +391 -391
- data/lib/rubydb/catalog/column.rb +112 -112
- data/lib/rubydb/catalog/constraint.rb +180 -180
- data/lib/rubydb/catalog/database.rb +184 -184
- data/lib/rubydb/catalog/index.rb +97 -97
- data/lib/rubydb/catalog/schema.rb +103 -103
- data/lib/rubydb/catalog/sequence.rb +90 -90
- data/lib/rubydb/catalog/system_catalog.rb +698 -698
- data/lib/rubydb/catalog/table.rb +178 -178
- data/lib/rubydb/catalog/trigger.rb +102 -102
- data/lib/rubydb/catalog/view.rb +66 -66
- data/lib/rubydb/cli/application.rb +168 -163
- data/lib/rubydb/cli/commands/accelerator.rb +72 -0
- data/lib/rubydb/cli/commands/backup.rb +80 -81
- data/lib/rubydb/cli/commands/branch.rb +72 -72
- data/lib/rubydb/cli/commands/checkout.rb +54 -54
- data/lib/rubydb/cli/commands/create.rb +58 -58
- data/lib/rubydb/cli/commands/diff.rb +76 -77
- data/lib/rubydb/cli/commands/doctor.rb +77 -74
- data/lib/rubydb/cli/commands/drop.rb +57 -57
- data/lib/rubydb/cli/commands/init.rb +101 -102
- data/lib/rubydb/cli/commands/inspect.rb +95 -95
- data/lib/rubydb/cli/commands/merge.rb +63 -63
- data/lib/rubydb/cli/commands/migrate.rb +62 -62
- data/lib/rubydb/cli/commands/restart.rb +42 -39
- data/lib/rubydb/cli/commands/restore.rb +121 -121
- data/lib/rubydb/cli/commands/shell.rb +365 -365
- data/lib/rubydb/cli/commands/snapshot.rb +79 -79
- data/lib/rubydb/cli/commands/start.rb +88 -82
- data/lib/rubydb/cli/commands/status.rb +96 -92
- data/lib/rubydb/cli/commands/stop.rb +47 -47
- data/lib/rubydb/cli/commands/vacuum.rb +58 -58
- data/lib/rubydb/cli/formatter.rb +221 -221
- data/lib/rubydb/cli/output.rb +168 -168
- data/lib/rubydb/client/client.rb +309 -304
- data/lib/rubydb/client/connection.rb +429 -415
- data/lib/rubydb/client/connection_pool.rb +168 -168
- data/lib/rubydb/client/connection_url.rb +96 -96
- data/lib/rubydb/client/prepared_statement.rb +60 -60
- data/lib/rubydb/client/result.rb +127 -123
- data/lib/rubydb/client/statement.rb +52 -52
- data/lib/rubydb/client/transaction.rb +130 -130
- data/lib/rubydb/concurrency/concurrency.rb +19 -19
- data/lib/rubydb/concurrency/deadlock_detector.rb +148 -150
- data/lib/rubydb/concurrency/latch.rb +101 -101
- data/lib/rubydb/concurrency/lock_graph.rb +163 -165
- data/lib/rubydb/concurrency/mutex.rb +181 -183
- data/lib/rubydb/concurrency/rw_lock.rb +180 -180
- data/lib/rubydb/concurrency/scheduler.rb +248 -250
- data/lib/rubydb/concurrency/worker_pool.rb +145 -143
- data/lib/rubydb/configuration/config.rb +170 -170
- data/lib/rubydb/configuration/defaults.rb +191 -179
- data/lib/rubydb/configuration/environment.rb +152 -152
- data/lib/rubydb/configuration/parser.rb +185 -185
- data/lib/rubydb/configuration/validation.rb +228 -221
- data/lib/rubydb/constants.rb +74 -74
- data/lib/rubydb/constraints/check.rb +181 -181
- data/lib/rubydb/constraints/constraint.rb +101 -101
- data/lib/rubydb/constraints/foreign_key.rb +130 -130
- data/lib/rubydb/constraints/not_null.rb +64 -64
- data/lib/rubydb/constraints/primary_key.rb +99 -99
- data/lib/rubydb/constraints/unique.rb +106 -108
- data/lib/rubydb/constraints/validator.rb +349 -350
- data/lib/rubydb/errors/authentication_error.rb +10 -10
- data/lib/rubydb/errors/authorization_error.rb +23 -23
- data/lib/rubydb/errors/client_error.rb +10 -10
- data/lib/rubydb/errors/configuration_error.rb +10 -10
- data/lib/rubydb/errors/connection_error.rb +10 -10
- data/lib/rubydb/errors/constraint_error.rb +23 -23
- data/lib/rubydb/errors/corruption_error.rb +10 -10
- data/lib/rubydb/errors/database_error.rb +10 -10
- data/lib/rubydb/errors/error.rb +20 -20
- data/lib/rubydb/errors/execution_error.rb +10 -10
- data/lib/rubydb/errors/parser_error.rb +10 -10
- data/lib/rubydb/errors/recovery_error.rb +10 -10
- data/lib/rubydb/errors/replication_error.rb +10 -10
- data/lib/rubydb/errors/server_error.rb +6 -6
- data/lib/rubydb/errors/storage_error.rb +10 -10
- data/lib/rubydb/errors/transaction_error.rb +10 -10
- data/lib/rubydb/execution/accelerator_dispatch.rb +30 -0
- data/lib/rubydb/execution/aggregate_executor.rb +134 -138
- data/lib/rubydb/execution/cost_model.rb +72 -0
- data/lib/rubydb/execution/delete_executor.rb +110 -112
- data/lib/rubydb/execution/distinct_executor.rb +131 -135
- data/lib/rubydb/execution/executor.rb +1544 -1188
- data/lib/rubydb/execution/expression.rb +191 -193
- data/lib/rubydb/execution/index_scan.rb +142 -142
- data/lib/rubydb/execution/insert_executor.rb +215 -217
- data/lib/rubydb/execution/join_executor.rb +243 -249
- data/lib/rubydb/execution/limit_executor.rb +83 -85
- data/lib/rubydb/execution/operator_selection.rb +57 -0
- data/lib/rubydb/execution/optimizer.rb +227 -215
- data/lib/rubydb/execution/physical_plan.rb +47 -0
- data/lib/rubydb/execution/plan.rb +355 -353
- data/lib/rubydb/execution/planner.rb +508 -536
- data/lib/rubydb/execution/predicate.rb +235 -235
- data/lib/rubydb/execution/scan.rb +49 -49
- data/lib/rubydb/execution/sequential_scan.rb +63 -63
- data/lib/rubydb/execution/sort_executor.rb +194 -185
- data/lib/rubydb/execution/update_executor.rb +160 -162
- data/lib/rubydb/functions/aggregate.rb +70 -70
- data/lib/rubydb/functions/date_functions.rb +274 -278
- data/lib/rubydb/functions/function.rb +85 -85
- data/lib/rubydb/functions/json_functions.rb +231 -215
- data/lib/rubydb/functions/numeric_functions.rb +346 -346
- data/lib/rubydb/functions/scalar.rb +52 -52
- data/lib/rubydb/functions/string_functions.rb +383 -383
- data/lib/rubydb/functions/system_functions.rb +258 -246
- data/lib/rubydb/history/as_of.rb +238 -238
- data/lib/rubydb/history/change.rb +105 -105
- data/lib/rubydb/history/history.rb +131 -131
- data/lib/rubydb/history/history_manager.rb +228 -229
- data/lib/rubydb/history/temporal_query.rb +202 -202
- data/lib/rubydb/history/timeline.rb +144 -144
- data/lib/rubydb/indexes/btree.rb +215 -186
- data/lib/rubydb/indexes/btree_cursor.rb +258 -258
- data/lib/rubydb/indexes/btree_node.rb +384 -385
- data/lib/rubydb/indexes/hash_index.rb +150 -150
- data/lib/rubydb/indexes/index.rb +71 -71
- data/lib/rubydb/indexes/index_manager.rb +408 -406
- data/lib/rubydb/indexes/index_scan.rb +466 -470
- data/lib/rubydb/migrations/migration.rb +253 -254
- data/lib/rubydb/migrations/migration_lock.rb +146 -146
- data/lib/rubydb/migrations/migration_manager.rb +187 -176
- data/lib/rubydb/migrations/migration_version.rb +71 -71
- data/lib/rubydb/migrations/schema_diff.rb +211 -211
- data/lib/rubydb/migrations/schema_version.rb +64 -64
- data/lib/rubydb/monitoring/events.rb +155 -160
- data/lib/rubydb/monitoring/health.rb +216 -222
- data/lib/rubydb/monitoring/logger.rb +188 -193
- data/lib/rubydb/monitoring/metrics.rb +363 -359
- data/lib/rubydb/monitoring/performance.rb +176 -176
- data/lib/rubydb/monitoring/statistics.rb +168 -170
- data/lib/rubydb/mvcc/garbage_collector.rb +199 -199
- data/lib/rubydb/mvcc/mvcc.rb +16 -16
- data/lib/rubydb/mvcc/snapshot.rb +146 -147
- data/lib/rubydb/mvcc/vacuum.rb +180 -180
- data/lib/rubydb/mvcc/version.rb +106 -106
- data/lib/rubydb/mvcc/version_store.rb +396 -398
- data/lib/rubydb/mvcc/visibility.rb +107 -109
- data/lib/rubydb/protocol/capabilities.rb +125 -125
- data/lib/rubydb/protocol/decoder.rb +142 -145
- data/lib/rubydb/protocol/encoder.rb +131 -136
- data/lib/rubydb/protocol/handshake.rb +306 -305
- data/lib/rubydb/protocol/message.rb +121 -121
- data/lib/rubydb/protocol/parameter_binder.rb +101 -0
- data/lib/rubydb/protocol/protocol.rb +276 -277
- data/lib/rubydb/protocol/version.rb +54 -54
- data/lib/rubydb/rails/adapter.rb +245 -239
- data/lib/rubydb/rails/connection.rb +312 -314
- data/lib/rubydb/rails/database_statements.rb +122 -122
- data/lib/rubydb/rails/migration.rb +131 -131
- data/lib/rubydb/rails/quoting.rb +109 -109
- data/lib/rubydb/rails/result.rb +117 -117
- data/lib/rubydb/rails/schema_statements.rb +339 -339
- data/lib/rubydb/rails/transaction.rb +105 -105
- data/lib/rubydb/rails/type.rb +126 -126
- data/lib/rubydb/recovery/checkpoint.rb +261 -257
- data/lib/rubydb/recovery/consistency.rb +457 -467
- data/lib/rubydb/recovery/corruption_detector.rb +5 -5
- data/lib/rubydb/recovery/crash_recovery.rb +381 -387
- data/lib/rubydb/recovery/recovery_manager.rb +204 -206
- data/lib/rubydb/recovery/redo.rb +235 -237
- data/lib/rubydb/recovery/undo.rb +204 -206
- data/lib/rubydb/replication/failover.rb +5 -5
- data/lib/rubydb/replication/fencing.rb +63 -63
- data/lib/rubydb/replication/primary.rb +461 -450
- data/lib/rubydb/replication/replica.rb +382 -384
- data/lib/rubydb/replication/replication_log.rb +194 -200
- data/lib/rubydb/replication/replication_manager.rb +307 -308
- data/lib/rubydb/replication/replication_slot.rb +293 -295
- data/lib/rubydb/replication/replication_stream.rb +198 -201
- data/lib/rubydb/rubydb.rb +570 -560
- data/lib/rubydb/security/access_control.rb +252 -254
- data/lib/rubydb/security/audit_log.rb +209 -213
- data/lib/rubydb/security/authentication.rb +302 -302
- data/lib/rubydb/security/authorization.rb +282 -282
- data/lib/rubydb/security/credentials.rb +192 -196
- data/lib/rubydb/security/password.rb +205 -215
- data/lib/rubydb/security/permissions.rb +74 -74
- data/lib/rubydb/security/role.rb +99 -101
- data/lib/rubydb/security/user.rb +86 -86
- data/lib/rubydb/server/connection.rb +383 -366
- data/lib/rubydb/server/connection_pool.rb +193 -193
- data/lib/rubydb/server/lifecycle.rb +227 -228
- data/lib/rubydb/server/listener.rb +139 -136
- data/lib/rubydb/server/request_handler.rb +277 -276
- data/lib/rubydb/server/server.rb +363 -364
- data/lib/rubydb/server/session.rb +416 -369
- data/lib/rubydb/server/worker.rb +206 -210
- data/lib/rubydb/server/worker_pool.rb +168 -168
- data/lib/rubydb/sql/ast/alter_table.rb +169 -169
- data/lib/rubydb/sql/ast/begin_transaction.rb +47 -47
- data/lib/rubydb/sql/ast/commit.rb +37 -37
- data/lib/rubydb/sql/ast/constraint.rb +92 -83
- data/lib/rubydb/sql/ast/create_database.rb +41 -41
- data/lib/rubydb/sql/ast/create_index.rb +61 -61
- data/lib/rubydb/sql/ast/create_schema.rb +52 -52
- data/lib/rubydb/sql/ast/create_table.rb +187 -187
- data/lib/rubydb/sql/ast/delete.rb +54 -54
- data/lib/rubydb/sql/ast/drop_database.rb +41 -41
- data/lib/rubydb/sql/ast/drop_index.rb +41 -41
- data/lib/rubydb/sql/ast/drop_schema.rb +49 -49
- data/lib/rubydb/sql/ast/drop_table.rb +49 -49
- data/lib/rubydb/sql/ast/explain.rb +64 -64
- data/lib/rubydb/sql/ast/expression.rb +617 -604
- data/lib/rubydb/sql/ast/insert.rb +66 -66
- data/lib/rubydb/sql/ast/node.rb +42 -42
- data/lib/rubydb/sql/ast/rollback.rb +63 -63
- data/lib/rubydb/sql/ast/savepoint.rb +59 -59
- data/lib/rubydb/sql/ast/select.rb +88 -88
- data/lib/rubydb/sql/ast/set_operation.rb +22 -20
- data/lib/rubydb/sql/ast/trigger.rb +35 -29
- data/lib/rubydb/sql/ast/update.rb +88 -88
- data/lib/rubydb/sql/ast/vacuum.rb +19 -19
- data/lib/rubydb/sql/ast/view.rb +38 -32
- data/lib/rubydb/sql/ast/with.rb +32 -32
- data/lib/rubydb/sql/grammar.rb +86 -86
- data/lib/rubydb/sql/keywords.rb +156 -156
- data/lib/rubydb/sql/lexer.rb +209 -214
- data/lib/rubydb/sql/operators.rb +100 -100
- data/lib/rubydb/sql/parser.rb +1167 -1170
- data/lib/rubydb/sql/planner/analyzer.rb +283 -302
- data/lib/rubydb/sql/planner/binder.rb +537 -550
- data/lib/rubydb/sql/planner/type_checker.rb +427 -431
- data/lib/rubydb/sql/token.rb +210 -210
- data/lib/rubydb/storage/buffer_frame.rb +44 -44
- data/lib/rubydb/storage/buffer_pool.rb +155 -155
- data/lib/rubydb/storage/database_lock.rb +74 -74
- data/lib/rubydb/storage/deserializer.rb +332 -342
- data/lib/rubydb/storage/engine.rb +2409 -2330
- data/lib/rubydb/storage/file_manager.rb +191 -187
- data/lib/rubydb/storage/free_space_map.rb +79 -81
- data/lib/rubydb/storage/page.rb +92 -94
- data/lib/rubydb/storage/page_allocator.rb +852 -855
- data/lib/rubydb/storage/page_header.rb +63 -67
- data/lib/rubydb/storage/page_manager.rb +127 -131
- data/lib/rubydb/storage/record.rb +58 -58
- data/lib/rubydb/storage/row.rb +78 -78
- data/lib/rubydb/storage/serializer.rb +51 -51
- data/lib/rubydb/storage/snapshot_reader.rb +167 -0
- data/lib/rubydb/storage/storage_layout.rb +151 -151
- data/lib/rubydb/storage/storage_manager.rb +114 -114
- data/lib/rubydb/storage/tuple.rb +458 -461
- data/lib/rubydb/storage/visibility_map.rb +964 -973
- data/lib/rubydb/transactions/commit_manager.rb +219 -220
- data/lib/rubydb/transactions/isolation.rb +98 -98
- data/lib/rubydb/transactions/lock.rb +76 -76
- data/lib/rubydb/transactions/lock_manager.rb +359 -362
- data/lib/rubydb/transactions/savepoint.rb +142 -143
- data/lib/rubydb/transactions/transaction.rb +214 -215
- data/lib/rubydb/transactions/transaction_id.rb +84 -84
- data/lib/rubydb/transactions/transaction_log.rb +256 -257
- data/lib/rubydb/transactions/transaction_manager.rb +434 -435
- data/lib/rubydb/types/bigint.rb +36 -36
- data/lib/rubydb/types/blob.rb +37 -37
- data/lib/rubydb/types/boolean.rb +34 -34
- data/lib/rubydb/types/date.rb +39 -39
- data/lib/rubydb/types/decimal.rb +48 -48
- data/lib/rubydb/types/float.rb +34 -34
- data/lib/rubydb/types/integer.rb +36 -36
- data/lib/rubydb/types/json.rb +41 -41
- data/lib/rubydb/types/null.rb +34 -34
- data/lib/rubydb/types/smallint.rb +36 -36
- data/lib/rubydb/types/text.rb +37 -37
- data/lib/rubydb/types/time.rb +46 -46
- data/lib/rubydb/types/timestamp.rb +39 -39
- data/lib/rubydb/types/type.rb +119 -119
- data/lib/rubydb/types/uuid.rb +47 -47
- data/lib/rubydb/types/varchar.rb +37 -37
- data/lib/rubydb/version.rb +32 -32
- data/lib/rubydb/wal/archive.rb +207 -193
- data/lib/rubydb/wal/checkpoint.rb +181 -183
- data/lib/rubydb/wal/lsn.rb +94 -94
- data/lib/rubydb/wal/reader.rb +259 -260
- data/lib/rubydb/wal/record.rb +105 -105
- data/lib/rubydb/wal/segment.rb +193 -193
- data/lib/rubydb/wal/wal.rb +481 -452
- data/lib/rubydb/wal/writer.rb +236 -236
- data/lib/rubydb.rb +7 -7
- data/packaging/docker/docker-compose.failover.yml +43 -43
- data/packaging/homebrew/rubydb.rb +19 -19
- data/rubydb.gemspec +70 -57
- data/scripts/benchmark +7 -7
- data/scripts/build_accelerator +49 -0
- data/scripts/durability_drill +37 -37
- data/scripts/fuzz +63 -63
- data/scripts/release +77 -42
- data/scripts/release_check +43 -43
- data/scripts/replication_failover_drill +268 -250
- data/scripts/replication_network_failover_drill +287 -255
- data/scripts/restore_drill +45 -45
- data/scripts/security +45 -0
- metadata +102 -1
data/ROADMAP.md
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
# RubyDB roadmap
|
|
2
|
-
|
|
3
|
-
## Current phase: validated foundation
|
|
4
|
-
|
|
5
|
-
RubyDB has a durable storage/WAL/recovery foundation, transactions and MVCC,
|
|
6
|
-
the documented SQL engine, server/client protocol, Rails adapter coverage,
|
|
7
|
-
backups, logical replication, monitoring, fuzzing, and release automation.
|
|
8
|
-
The latest local audit passed 270 examples with zero failures.
|
|
9
|
-
|
|
10
|
-
## Next gates
|
|
11
|
-
|
|
12
|
-
1. Retain hosted Ruby/Rails/OS matrix evidence for each release.
|
|
13
|
-
2. Run physical filesystem quota, power-loss, corruption, restore, and compaction
|
|
14
|
-
drills on deployment targets.
|
|
15
|
-
3. Validate multi-host replication partitions, independent fencing, stale
|
|
16
|
-
primary rejection, and operator promotion procedures.
|
|
17
|
-
4. Complete certificate/secret rotation validation and an independent security
|
|
18
|
-
review.
|
|
19
|
-
5. Establish workload-specific latency, throughput, capacity, RPO, and RTO
|
|
20
|
-
baselines.
|
|
21
|
-
|
|
22
|
-
## Longer term
|
|
23
|
-
|
|
24
|
-
Expand the documented SQLite-style profile and only then consider broader
|
|
25
|
-
PostgreSQL/MySQL compatibility where implementation and semantic validation can
|
|
26
|
-
be maintained. Add automatic election only with an independently tested fencing
|
|
27
|
-
authority. Preserve correctness and explicit unsupported errors as hard gates.
|
|
1
|
+
# RubyDB roadmap
|
|
2
|
+
|
|
3
|
+
## Current phase: validated foundation
|
|
4
|
+
|
|
5
|
+
RubyDB has a durable storage/WAL/recovery foundation, transactions and MVCC,
|
|
6
|
+
the documented SQL engine, server/client protocol, Rails adapter coverage,
|
|
7
|
+
backups, logical replication, monitoring, fuzzing, and release automation.
|
|
8
|
+
The latest local audit passed 270 examples with zero failures.
|
|
9
|
+
|
|
10
|
+
## Next gates
|
|
11
|
+
|
|
12
|
+
1. Retain hosted Ruby/Rails/OS matrix evidence for each release.
|
|
13
|
+
2. Run physical filesystem quota, power-loss, corruption, restore, and compaction
|
|
14
|
+
drills on deployment targets.
|
|
15
|
+
3. Validate multi-host replication partitions, independent fencing, stale
|
|
16
|
+
primary rejection, and operator promotion procedures.
|
|
17
|
+
4. Complete certificate/secret rotation validation and an independent security
|
|
18
|
+
review.
|
|
19
|
+
5. Establish workload-specific latency, throughput, capacity, RPO, and RTO
|
|
20
|
+
baselines.
|
|
21
|
+
|
|
22
|
+
## Longer term
|
|
23
|
+
|
|
24
|
+
Expand the documented SQLite-style profile and only then consider broader
|
|
25
|
+
PostgreSQL/MySQL compatibility where implementation and semantic validation can
|
|
26
|
+
be maintained. Add automatic election only with an independently tested fencing
|
|
27
|
+
authority. Preserve correctness and explicit unsupported errors as hard gates.
|
data/Rakefile
CHANGED
|
@@ -1,71 +1,76 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
|
|
9
|
-
RSpec::Core::RakeTask.new(:spec)
|
|
10
|
-
|
|
11
|
-
task default: %i[spec rubocop]
|
|
12
|
-
|
|
13
|
-
namespace :build do
|
|
14
|
-
desc
|
|
15
|
-
task
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
desc
|
|
35
|
-
RSpec::Core::RakeTask.new(:
|
|
36
|
-
t.pattern =
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
desc
|
|
53
|
-
RSpec::Core::RakeTask.new(:
|
|
54
|
-
t.pattern =
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
desc
|
|
58
|
-
RSpec::Core::RakeTask.new(:
|
|
59
|
-
t.pattern =
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
desc
|
|
69
|
-
task :
|
|
70
|
-
|
|
71
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
require "rbconfig"
|
|
6
|
+
require "digest"
|
|
7
|
+
require "fileutils"
|
|
8
|
+
|
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
10
|
+
|
|
11
|
+
task default: %i[spec rubocop]
|
|
12
|
+
|
|
13
|
+
namespace :build do
|
|
14
|
+
desc "Build the bundled Go accelerator for the current platform"
|
|
15
|
+
task :accelerator do
|
|
16
|
+
abort "Go accelerator build failed" unless system({"RUBYDB_ACCELERATOR_TARGETS" => "current"}, RbConfig.ruby, "scripts/build_accelerator")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
desc "Build the gem and write a SHA-512 checksum for the exact artifact"
|
|
20
|
+
task checksum: [:accelerator, :build] do
|
|
21
|
+
gem_path = Dir["pkg/rubydb-*.gem"].max_by { |path| File.mtime(path) }
|
|
22
|
+
abort "No built gem found in pkg/" unless gem_path
|
|
23
|
+
|
|
24
|
+
FileUtils.mkdir_p("checksums")
|
|
25
|
+
checksum_path = File.join("checksums", "#{File.basename(gem_path)}.sha512")
|
|
26
|
+
File.write(checksum_path, "#{Digest::SHA512.file(gem_path).hexdigest} #{File.basename(gem_path)}\n")
|
|
27
|
+
puts "Wrote #{checksum_path}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
desc "Run all tests"
|
|
32
|
+
task test: :spec
|
|
33
|
+
|
|
34
|
+
desc "Run unit tests only"
|
|
35
|
+
RSpec::Core::RakeTask.new(:"test:unit") do |t|
|
|
36
|
+
t.pattern = "test/unit/**/*_spec.rb"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc "Run integration tests only"
|
|
40
|
+
RSpec::Core::RakeTask.new(:"test:integration") do |t|
|
|
41
|
+
t.pattern = "test/integration/**/*_spec.rb"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
require "rubocop/rake_task"
|
|
46
|
+
RuboCop::RakeTask.new
|
|
47
|
+
rescue LoadError
|
|
48
|
+
# rubocop not installed
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
namespace :test do
|
|
52
|
+
desc "Run concurrency tests"
|
|
53
|
+
RSpec::Core::RakeTask.new(:concurrency) do |t|
|
|
54
|
+
t.pattern = "test/concurrency/**/*_spec.rb"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
desc "Run crash tests"
|
|
58
|
+
RSpec::Core::RakeTask.new(:crash) do |t|
|
|
59
|
+
t.pattern = "test/crash/**/*_spec.rb"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
desc "Run chaos tests"
|
|
63
|
+
RSpec::Core::RakeTask.new(:chaos) do |t|
|
|
64
|
+
t.pattern = "test/chaos/**/*_spec.rb"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
desc "Run benchmarks"
|
|
69
|
+
task :benchmark do
|
|
70
|
+
Dir["benchmarks/**/*.rb"].each { |f| load f }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
desc "Run the concurrent workload and durability verification"
|
|
74
|
+
task :workload do
|
|
75
|
+
abort "Concurrent workload failed" unless system(RbConfig.ruby, "benchmarks/concurrent_workload.rb")
|
|
76
|
+
end
|
data/SECURITY.md
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
# RubyDB security policy
|
|
2
|
-
|
|
3
|
-
RubyDB is alpha software with a documented, tested feature set. Security
|
|
4
|
-
controls are implemented and tested for the server protocol, TLS transport,
|
|
5
|
-
password/SCRAM authentication, authorization, bounded frames, query deadlines,
|
|
6
|
-
resource limits, replication tokens, path validation, backup manifests, and
|
|
7
|
-
fencing. A deployment must still complete an independent security review
|
|
8
|
-
before it handles sensitive or regulated data.
|
|
9
|
-
|
|
10
|
-
## Secure deployment requirements
|
|
11
|
-
|
|
12
|
-
- Keep the database directory, WAL, backups, TLS private keys, authentication
|
|
13
|
-
credentials, and replication state on protected storage with least-privilege
|
|
14
|
-
filesystem ownership.
|
|
15
|
-
- Bind the server and replication listeners to explicit private addresses. Do
|
|
16
|
-
not expose them directly to the public internet; restrict ingress with a
|
|
17
|
-
firewall or private network.
|
|
18
|
-
- Enable TLS with TLS 1.2 or newer, validate the server certificate, and use a
|
|
19
|
-
trusted CA or mutual TLS where the deployment requires peer identity.
|
|
20
|
-
- Use high-entropy credentials and a separate high-entropy replication token.
|
|
21
|
-
Never put secrets in source control, URLs, shell history, logs, or bug
|
|
22
|
-
reports.
|
|
23
|
-
- Configure maximum connections, request/frame sizes, query deadlines, and
|
|
24
|
-
filesystem thresholds for the host. Treat readiness failure and recovery-
|
|
25
|
-
required acknowledgements as incidents.
|
|
26
|
-
- Run backups with encryption approved by the organization, retain the
|
|
27
|
-
manifest and WAL chain together, and perform restore drills into a new
|
|
28
|
-
inactive directory.
|
|
29
|
-
|
|
30
|
-
## Rotation and incident response
|
|
31
|
-
|
|
32
|
-
Stage replacement certificates and credentials, validate them on a non-serving
|
|
33
|
-
instance, then rotate during a planned window. Drain clients or replication
|
|
34
|
-
peers, change both sides of a replication token, reconnect, and verify the
|
|
35
|
-
authentication-failure metric returns to baseline. Retain the old certificate
|
|
36
|
-
only for the documented overlap window and remove it afterward.
|
|
37
|
-
|
|
38
|
-
For suspected compromise, corruption, checksum failure, replication
|
|
39
|
-
divergence, or fencing anomalies: stop writes, isolate the affected node,
|
|
40
|
-
preserve the database/WAL/log files and timestamps, and create a verified copy
|
|
41
|
-
before attempting repair, vacuum, restore, or promotion. Automatic election is
|
|
42
|
-
disabled; never run two writable primaries without an externally verified
|
|
43
|
-
fencing lease.
|
|
44
|
-
|
|
45
|
-
## Reporting a vulnerability
|
|
46
|
-
|
|
47
|
-
Do not disclose unpatched vulnerabilities in a public issue. Use a private
|
|
48
|
-
GitHub security advisory for the repository or contact the maintainer listed in
|
|
49
|
-
the gem metadata with reproduction steps, affected versions, impact, and a
|
|
50
|
-
safe disclosure timeline. Do not include production secrets or customer data.
|
|
51
|
-
|
|
52
|
-
Security scans run in GitHub Actions, but scan results do not replace review of
|
|
53
|
-
deployment configuration, network policy, secret storage, certificate
|
|
54
|
-
lifecycle, or the host filesystem.
|
|
1
|
+
# RubyDB security policy
|
|
2
|
+
|
|
3
|
+
RubyDB is alpha software with a documented, tested feature set. Security
|
|
4
|
+
controls are implemented and tested for the server protocol, TLS transport,
|
|
5
|
+
password/SCRAM authentication, authorization, bounded frames, query deadlines,
|
|
6
|
+
resource limits, replication tokens, path validation, backup manifests, and
|
|
7
|
+
fencing. A deployment must still complete an independent security review
|
|
8
|
+
before it handles sensitive or regulated data.
|
|
9
|
+
|
|
10
|
+
## Secure deployment requirements
|
|
11
|
+
|
|
12
|
+
- Keep the database directory, WAL, backups, TLS private keys, authentication
|
|
13
|
+
credentials, and replication state on protected storage with least-privilege
|
|
14
|
+
filesystem ownership.
|
|
15
|
+
- Bind the server and replication listeners to explicit private addresses. Do
|
|
16
|
+
not expose them directly to the public internet; restrict ingress with a
|
|
17
|
+
firewall or private network.
|
|
18
|
+
- Enable TLS with TLS 1.2 or newer, validate the server certificate, and use a
|
|
19
|
+
trusted CA or mutual TLS where the deployment requires peer identity.
|
|
20
|
+
- Use high-entropy credentials and a separate high-entropy replication token.
|
|
21
|
+
Never put secrets in source control, URLs, shell history, logs, or bug
|
|
22
|
+
reports.
|
|
23
|
+
- Configure maximum connections, request/frame sizes, query deadlines, and
|
|
24
|
+
filesystem thresholds for the host. Treat readiness failure and recovery-
|
|
25
|
+
required acknowledgements as incidents.
|
|
26
|
+
- Run backups with encryption approved by the organization, retain the
|
|
27
|
+
manifest and WAL chain together, and perform restore drills into a new
|
|
28
|
+
inactive directory.
|
|
29
|
+
|
|
30
|
+
## Rotation and incident response
|
|
31
|
+
|
|
32
|
+
Stage replacement certificates and credentials, validate them on a non-serving
|
|
33
|
+
instance, then rotate during a planned window. Drain clients or replication
|
|
34
|
+
peers, change both sides of a replication token, reconnect, and verify the
|
|
35
|
+
authentication-failure metric returns to baseline. Retain the old certificate
|
|
36
|
+
only for the documented overlap window and remove it afterward.
|
|
37
|
+
|
|
38
|
+
For suspected compromise, corruption, checksum failure, replication
|
|
39
|
+
divergence, or fencing anomalies: stop writes, isolate the affected node,
|
|
40
|
+
preserve the database/WAL/log files and timestamps, and create a verified copy
|
|
41
|
+
before attempting repair, vacuum, restore, or promotion. Automatic election is
|
|
42
|
+
disabled; never run two writable primaries without an externally verified
|
|
43
|
+
fencing lease.
|
|
44
|
+
|
|
45
|
+
## Reporting a vulnerability
|
|
46
|
+
|
|
47
|
+
Do not disclose unpatched vulnerabilities in a public issue. Use a private
|
|
48
|
+
GitHub security advisory for the repository or contact the maintainer listed in
|
|
49
|
+
the gem metadata with reproduction steps, affected versions, impact, and a
|
|
50
|
+
safe disclosure timeline. Do not include production secrets or customer data.
|
|
51
|
+
|
|
52
|
+
Security scans run in GitHub Actions, but scan results do not replace review of
|
|
53
|
+
deployment configuration, network policy, secret storage, certificate
|
|
54
|
+
lifecycle, or the host filesystem.
|
data/SUPPORT.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# RubyDB support policy
|
|
2
|
-
|
|
3
|
-
RubyDB currently supports the documented RubyDB SQL surface, the tested common
|
|
4
|
-
SQLite-style profile, the Ruby client/server path, and the ActiveRecord adapter
|
|
5
|
-
combinations listed by CI. It does not promise complete PostgreSQL, MySQL, or
|
|
6
|
-
SQLite dialect, file-format, extension, or wire compatibility.
|
|
7
|
-
|
|
8
|
-
When requesting help, include RubyDB commit/version, Ruby and Rails versions,
|
|
9
|
-
OS, topology, configuration shape without secrets, a minimal reproduction, and
|
|
10
|
-
the exact error. Include workload and recovery evidence for data-safety issues.
|
|
11
|
-
|
|
12
|
-
For corruption, checksum, replication divergence, or fencing anomalies, stop
|
|
13
|
-
writes and preserve the database, WAL, logs, and timestamps before attempting
|
|
14
|
-
repair. Report unpatched security issues privately through `SECURITY.md`.
|
|
1
|
+
# RubyDB support policy
|
|
2
|
+
|
|
3
|
+
RubyDB currently supports the documented RubyDB SQL surface, the tested common
|
|
4
|
+
SQLite-style profile, the Ruby client/server path, and the ActiveRecord adapter
|
|
5
|
+
combinations listed by CI. It does not promise complete PostgreSQL, MySQL, or
|
|
6
|
+
SQLite dialect, file-format, extension, or wire compatibility.
|
|
7
|
+
|
|
8
|
+
When requesting help, include RubyDB commit/version, Ruby and Rails versions,
|
|
9
|
+
OS, topology, configuration shape without secrets, a minimal reproduction, and
|
|
10
|
+
the exact error. Include workload and recovery evidence for data-safety issues.
|
|
11
|
+
|
|
12
|
+
For corruption, checksum, replication divergence, or fencing anomalies, stop
|
|
13
|
+
writes and preserve the database, WAL, logs, and timestamps before attempting
|
|
14
|
+
repair. Report unpatched security issues privately through `SECURITY.md`.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
ceeaad8692cfdc0e7b72b85d2c17b5e23c754d5f1159590df142fcfa857b8ff3 rubydb-accelerator-darwin-amd64
|
|
2
|
+
1ab2ca6b7294f7d97ff4e816a5ad504802e84e4c196146a199a145a74829e3a0 rubydb-accelerator-darwin-arm64
|
|
3
|
+
a0d141e3c41f2709d5bbaa67dd856f28805e4c2cbbc1034826e190877aa05aad rubydb-accelerator-linux-amd64
|
|
4
|
+
445b164c71284ce6432215fcf1213a99497194b3c8da6ea9577602405de4cf38 rubydb-accelerator-linux-arm64
|
|
5
|
+
9e5c4f9acc9fe9751d5c945956fdbec50f9c698a2b5a0366255edef700a1499d rubydb-accelerator-windows-amd64.exe
|
|
6
|
+
3dd6aa97761f00a4552fa21a16571598bdfef8850347202ee4131d88ee97d002 rubydb-accelerator-windows-arm64.exe
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/accelerator/go.mod
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
package execution
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"strings"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
func AggregateRows(rows []map[string]interface{}, groupBy []string, definitions []Aggregate) []map[string]interface{} {
|
|
9
|
+
groups := map[string][]map[string]interface{}{}
|
|
10
|
+
keys := map[string][]interface{}{}
|
|
11
|
+
order := make([]string, 0)
|
|
12
|
+
if len(groupBy) == 0 {
|
|
13
|
+
groups["_"] = rows
|
|
14
|
+
order = append(order, "_")
|
|
15
|
+
} else {
|
|
16
|
+
for _, row := range rows {
|
|
17
|
+
values := make([]interface{}, len(groupBy))
|
|
18
|
+
for i, column := range groupBy {
|
|
19
|
+
values[i] = row[column]
|
|
20
|
+
}
|
|
21
|
+
keyBytes, _ := json.Marshal(values)
|
|
22
|
+
key := string(keyBytes)
|
|
23
|
+
if _, exists := groups[key]; !exists {
|
|
24
|
+
order = append(order, key)
|
|
25
|
+
}
|
|
26
|
+
groups[key] = append(groups[key], row)
|
|
27
|
+
keys[key] = values
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
result := make([]map[string]interface{}, 0, len(groups))
|
|
31
|
+
for _, key := range order {
|
|
32
|
+
group := groups[key]
|
|
33
|
+
row := map[string]interface{}{}
|
|
34
|
+
for i, column := range groupBy {
|
|
35
|
+
row[column] = keys[key][i]
|
|
36
|
+
}
|
|
37
|
+
for _, definition := range definitions {
|
|
38
|
+
name := definition.Alias
|
|
39
|
+
if name == "" {
|
|
40
|
+
name = strings.ToLower(definition.Function) + "(" + definition.Column + ")"
|
|
41
|
+
}
|
|
42
|
+
row[name] = AggregateValue(group, definition)
|
|
43
|
+
}
|
|
44
|
+
result = append(result, row)
|
|
45
|
+
}
|
|
46
|
+
return result
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func AggregateValue(rows []map[string]interface{}, definition Aggregate) interface{} {
|
|
50
|
+
values := make([]interface{}, 0, len(rows))
|
|
51
|
+
for _, row := range rows {
|
|
52
|
+
value := row[definition.Column]
|
|
53
|
+
if value != nil {
|
|
54
|
+
values = append(values, value)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
switch strings.ToUpper(definition.Function) {
|
|
58
|
+
case "COUNT":
|
|
59
|
+
if definition.Column == "*" {
|
|
60
|
+
return len(rows)
|
|
61
|
+
}
|
|
62
|
+
return len(values)
|
|
63
|
+
case "SUM", "AVG":
|
|
64
|
+
var sum float64
|
|
65
|
+
for _, value := range values {
|
|
66
|
+
number, ok := NumberValue(value)
|
|
67
|
+
if !ok {
|
|
68
|
+
return nil
|
|
69
|
+
}
|
|
70
|
+
sum += number
|
|
71
|
+
}
|
|
72
|
+
if strings.EqualFold(definition.Function, "AVG") {
|
|
73
|
+
if len(values) == 0 {
|
|
74
|
+
return nil
|
|
75
|
+
}
|
|
76
|
+
return sum / float64(len(values))
|
|
77
|
+
}
|
|
78
|
+
return sum
|
|
79
|
+
case "MIN", "MAX":
|
|
80
|
+
if len(values) == 0 {
|
|
81
|
+
return nil
|
|
82
|
+
}
|
|
83
|
+
best := values[0]
|
|
84
|
+
for _, value := range values[1:] {
|
|
85
|
+
comparison := Compare(value, best)
|
|
86
|
+
if (strings.EqualFold(definition.Function, "MIN") && comparison < 0) || (strings.EqualFold(definition.Function, "MAX") && comparison > 0) {
|
|
87
|
+
best = value
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return best
|
|
91
|
+
default:
|
|
92
|
+
return nil
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package execution
|
|
2
|
+
|
|
3
|
+
import "encoding/json"
|
|
4
|
+
|
|
5
|
+
func DistinctRows(rows []map[string]interface{}, columns []string) []map[string]interface{} {
|
|
6
|
+
seen := make(map[string]struct{}, len(rows))
|
|
7
|
+
result := make([]map[string]interface{}, 0, len(rows))
|
|
8
|
+
for _, row := range rows {
|
|
9
|
+
values := make([]interface{}, len(columns))
|
|
10
|
+
for index, column := range columns {
|
|
11
|
+
values[index] = row[column]
|
|
12
|
+
}
|
|
13
|
+
keyBytes, _ := json.Marshal(values)
|
|
14
|
+
key := string(keyBytes)
|
|
15
|
+
if _, exists := seen[key]; exists {
|
|
16
|
+
continue
|
|
17
|
+
}
|
|
18
|
+
seen[key] = struct{}{}
|
|
19
|
+
result = append(result, row)
|
|
20
|
+
}
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package execution
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"regexp"
|
|
6
|
+
"strings"
|
|
7
|
+
|
|
8
|
+
"github.com/aldanedev-create/rubydb/accelerator/internal/parallel"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
func FilterRows(rows []map[string]interface{}, condition Filter) []map[string]interface{} {
|
|
12
|
+
if len(rows) < 4096 {
|
|
13
|
+
filtered := make([]map[string]interface{}, 0, len(rows))
|
|
14
|
+
for _, row := range rows {
|
|
15
|
+
if Matches(row[condition.Column], condition.Operator, condition.Value) {
|
|
16
|
+
filtered = append(filtered, row)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return filtered
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
chunks := make([][]map[string]interface{}, parallel.ChunkCount(len(rows)))
|
|
23
|
+
parallel.ForEachChunk(len(rows), func(index, start, end int) {
|
|
24
|
+
filtered := make([]map[string]interface{}, 0, end-start)
|
|
25
|
+
for _, row := range rows[start:end] {
|
|
26
|
+
if Matches(row[condition.Column], condition.Operator, condition.Value) {
|
|
27
|
+
filtered = append(filtered, row)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
chunks[index] = filtered
|
|
31
|
+
})
|
|
32
|
+
filtered := make([]map[string]interface{}, 0, len(rows))
|
|
33
|
+
for _, chunk := range chunks {
|
|
34
|
+
filtered = append(filtered, chunk...)
|
|
35
|
+
}
|
|
36
|
+
return filtered
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func Matches(actual interface{}, operator string, expected interface{}) bool {
|
|
40
|
+
if strings.EqualFold(operator, "is_null") {
|
|
41
|
+
return actual == nil
|
|
42
|
+
}
|
|
43
|
+
if strings.EqualFold(operator, "is_not_null") {
|
|
44
|
+
return actual != nil
|
|
45
|
+
}
|
|
46
|
+
if actual == nil || expected == nil {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
if strings.EqualFold(operator, "like") {
|
|
50
|
+
pattern := regexp.QuoteMeta(fmt.Sprint(expected))
|
|
51
|
+
pattern = strings.ReplaceAll(pattern, "%", ".*")
|
|
52
|
+
pattern = strings.ReplaceAll(pattern, "_", ".")
|
|
53
|
+
matched, _ := regexp.MatchString("(?i)^"+pattern+"$", fmt.Sprint(actual))
|
|
54
|
+
return matched
|
|
55
|
+
}
|
|
56
|
+
comparison := Compare(actual, expected)
|
|
57
|
+
switch strings.ToLower(operator) {
|
|
58
|
+
case "eq":
|
|
59
|
+
return comparison == 0
|
|
60
|
+
case "ne":
|
|
61
|
+
return comparison != 0
|
|
62
|
+
case "lt":
|
|
63
|
+
return comparison < 0
|
|
64
|
+
case "lte":
|
|
65
|
+
return comparison <= 0
|
|
66
|
+
case "gt":
|
|
67
|
+
return comparison > 0
|
|
68
|
+
case "gte":
|
|
69
|
+
return comparison >= 0
|
|
70
|
+
default:
|
|
71
|
+
return false
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package execution
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"sort"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
func HashJoinRows(leftRows, rightRows []map[string]interface{}, leftKey, rightKey string) []map[string]interface{} {
|
|
9
|
+
index := make(map[string][]map[string]interface{}, len(rightRows))
|
|
10
|
+
for _, row := range rightRows {
|
|
11
|
+
key := JoinKey(row[rightKey])
|
|
12
|
+
index[key] = append(index[key], row)
|
|
13
|
+
}
|
|
14
|
+
rows := make([]map[string]interface{}, 0)
|
|
15
|
+
for _, left := range leftRows {
|
|
16
|
+
for _, right := range index[JoinKey(left[leftKey])] {
|
|
17
|
+
merged := make(map[string]interface{}, len(left)+len(right))
|
|
18
|
+
for key, value := range right {
|
|
19
|
+
merged[key] = value
|
|
20
|
+
}
|
|
21
|
+
for key, value := range left {
|
|
22
|
+
merged[key] = value
|
|
23
|
+
}
|
|
24
|
+
rows = append(rows, merged)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return rows
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// MergeJoinRows is used when both inputs are already ordered by their join
|
|
31
|
+
// keys. It avoids allocating a hash table for large, sorted relations.
|
|
32
|
+
func MergeJoinRows(leftRows, rightRows []map[string]interface{}, leftKey, rightKey string) []map[string]interface{} {
|
|
33
|
+
left := append([]map[string]interface{}(nil), leftRows...)
|
|
34
|
+
right := append([]map[string]interface{}(nil), rightRows...)
|
|
35
|
+
sort.SliceStable(left, func(i, j int) bool { return Compare(left[i][leftKey], left[j][leftKey]) < 0 })
|
|
36
|
+
sort.SliceStable(right, func(i, j int) bool { return Compare(right[i][rightKey], right[j][rightKey]) < 0 })
|
|
37
|
+
result := make([]map[string]interface{}, 0)
|
|
38
|
+
for i, j := 0, 0; i < len(left) && j < len(right); {
|
|
39
|
+
comparison := Compare(left[i][leftKey], right[j][rightKey])
|
|
40
|
+
if comparison < 0 {
|
|
41
|
+
i++
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
if comparison > 0 {
|
|
45
|
+
j++
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
leftEnd, rightEnd := i, j
|
|
49
|
+
for leftEnd < len(left) && Compare(left[leftEnd][leftKey], left[i][leftKey]) == 0 {
|
|
50
|
+
leftEnd++
|
|
51
|
+
}
|
|
52
|
+
for rightEnd < len(right) && Compare(right[rightEnd][rightKey], right[j][rightKey]) == 0 {
|
|
53
|
+
rightEnd++
|
|
54
|
+
}
|
|
55
|
+
for leftIndex := i; leftIndex < leftEnd; leftIndex++ {
|
|
56
|
+
for rightIndex := j; rightIndex < rightEnd; rightIndex++ {
|
|
57
|
+
result = append(result, mergeRows(left[leftIndex], right[rightIndex]))
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
i, j = leftEnd, rightEnd
|
|
61
|
+
}
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func mergeRows(left, right map[string]interface{}) map[string]interface{} {
|
|
66
|
+
merged := make(map[string]interface{}, len(left)+len(right))
|
|
67
|
+
for key, value := range right {
|
|
68
|
+
merged[key] = value
|
|
69
|
+
}
|
|
70
|
+
for key, value := range left {
|
|
71
|
+
merged[key] = value
|
|
72
|
+
}
|
|
73
|
+
return merged
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
func JoinKey(value interface{}) string {
|
|
77
|
+
encoded, _ := json.Marshal(value)
|
|
78
|
+
return string(encoded)
|
|
79
|
+
}
|