rubydb 0.1.4 → 0.1.5
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 +12 -5
- data/.rubocop.yml +50 -44
- data/.standard.yml +9 -14
- data/ARCHITECTURE.md +21 -21
- data/CHANGELOG.md +43 -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 +249 -162
- data/ROADMAP.md +27 -27
- data/Rakefile +71 -71
- data/SECURITY.md +54 -54
- data/SUPPORT.md +14 -14
- data/adapters/activerecord/Gemfile +11 -11
- data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +873 -879
- data/adapters/activerecord/rubydb-activerecord.gemspec +20 -20
- 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 +75 -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/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 +275 -275
- data/docs/contributing/architecture.md +9 -9
- data/docs/contributing/benchmarking.md +14 -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 +17 -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 +94 -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 +125 -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 +163 -163
- 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 +74 -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 +302 -304
- data/lib/rubydb/client/connection.rb +414 -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 +123 -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 +179 -179
- data/lib/rubydb/configuration/environment.rb +152 -152
- data/lib/rubydb/configuration/parser.rb +185 -185
- data/lib/rubydb/configuration/validation.rb +221 -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/aggregate_executor.rb +134 -138
- data/lib/rubydb/execution/delete_executor.rb +110 -112
- data/lib/rubydb/execution/distinct_executor.rb +131 -135
- data/lib/rubydb/execution/executor.rb +1182 -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/optimizer.rb +227 -215
- data/lib/rubydb/execution/plan.rb +355 -353
- data/lib/rubydb/execution/planner.rb +542 -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 +180 -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 +186 -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 +564 -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 +371 -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 +2347 -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/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 +190 -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 +480 -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 +60 -57
- data/scripts/benchmark +7 -7
- data/scripts/durability_drill +37 -37
- data/scripts/fuzz +63 -63
- data/scripts/release +45 -40
- 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 +54 -1
|
@@ -1,383 +1,383 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "digest"
|
|
4
|
-
require "base64"
|
|
5
|
-
|
|
6
|
-
module RubyDB
|
|
7
|
-
module Functions
|
|
8
|
-
# StringFunctions - All string-related functions
|
|
9
|
-
class StringFunctions
|
|
10
|
-
# LOWER - Convert string to lowercase
|
|
11
|
-
class Lower < ScalarFunction
|
|
12
|
-
def initialize
|
|
13
|
-
super(:lower,
|
|
14
|
-
description: "Convert string to lowercase",
|
|
15
|
-
category: :string,
|
|
16
|
-
min_args: 1,
|
|
17
|
-
max_args: 1,
|
|
18
|
-
return_type: :text,
|
|
19
|
-
deterministic: true,
|
|
20
|
-
immutable: true
|
|
21
|
-
)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def execute_scalar(args)
|
|
25
|
-
return nil if args[0].nil?
|
|
26
|
-
args[0].to_s.downcase
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# UPPER - Convert string to uppercase
|
|
31
|
-
class Upper < ScalarFunction
|
|
32
|
-
def initialize
|
|
33
|
-
super(:upper,
|
|
34
|
-
description: "Convert string to uppercase",
|
|
35
|
-
category: :string,
|
|
36
|
-
min_args: 1,
|
|
37
|
-
max_args: 1,
|
|
38
|
-
return_type: :text,
|
|
39
|
-
deterministic: true,
|
|
40
|
-
immutable: true
|
|
41
|
-
)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def execute_scalar(args)
|
|
45
|
-
return nil if args[0].nil?
|
|
46
|
-
args[0].to_s.upcase
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# LENGTH - Get string length
|
|
51
|
-
class Length < ScalarFunction
|
|
52
|
-
def initialize
|
|
53
|
-
super(:length,
|
|
54
|
-
description: "Get string length",
|
|
55
|
-
category: :string,
|
|
56
|
-
min_args: 1,
|
|
57
|
-
max_args: 1,
|
|
58
|
-
return_type: :integer,
|
|
59
|
-
deterministic: true,
|
|
60
|
-
immutable: true
|
|
61
|
-
)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def execute_scalar(args)
|
|
65
|
-
return nil if args[0].nil?
|
|
66
|
-
args[0].to_s.length
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# SUBSTR - Extract substring
|
|
71
|
-
class Substr < ScalarFunction
|
|
72
|
-
def initialize
|
|
73
|
-
super(:substr,
|
|
74
|
-
description: "Extract substring",
|
|
75
|
-
category: :string,
|
|
76
|
-
min_args: 2,
|
|
77
|
-
max_args: 3,
|
|
78
|
-
return_type: :text,
|
|
79
|
-
deterministic: true,
|
|
80
|
-
immutable: true
|
|
81
|
-
)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def execute_scalar(args)
|
|
85
|
-
return nil if args[0].nil? || args[1].nil?
|
|
86
|
-
|
|
87
|
-
str = args[0].to_s
|
|
88
|
-
start_pos = args[1].to_i
|
|
89
|
-
length = args[2] ? args[2].to_i : str.length
|
|
90
|
-
|
|
91
|
-
start_pos -= 1 if start_pos > 0
|
|
92
|
-
str[start_pos, length]
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# CONCAT - Concatenate strings
|
|
97
|
-
class Concat < ScalarFunction
|
|
98
|
-
def initialize
|
|
99
|
-
super(:concat,
|
|
100
|
-
description: "Concatenate strings",
|
|
101
|
-
category: :string,
|
|
102
|
-
min_args: 1,
|
|
103
|
-
max_args: -1,
|
|
104
|
-
return_type: :text,
|
|
105
|
-
deterministic: true,
|
|
106
|
-
immutable: true
|
|
107
|
-
)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def execute_scalar(args)
|
|
111
|
-
args.map { |a| a.to_s }.join
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# TRIM - Remove whitespace
|
|
116
|
-
class Trim < ScalarFunction
|
|
117
|
-
def initialize
|
|
118
|
-
super(:trim,
|
|
119
|
-
description: "Remove leading and trailing whitespace",
|
|
120
|
-
category: :string,
|
|
121
|
-
min_args: 1,
|
|
122
|
-
max_args: 1,
|
|
123
|
-
return_type: :text,
|
|
124
|
-
deterministic: true,
|
|
125
|
-
immutable: true
|
|
126
|
-
)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def execute_scalar(args)
|
|
130
|
-
return nil if args[0].nil?
|
|
131
|
-
args[0].to_s.strip
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# LTRIM - Remove leading whitespace
|
|
136
|
-
class Ltrim < ScalarFunction
|
|
137
|
-
def initialize
|
|
138
|
-
super(:ltrim,
|
|
139
|
-
description: "Remove leading whitespace",
|
|
140
|
-
category: :string,
|
|
141
|
-
min_args: 1,
|
|
142
|
-
max_args: 1,
|
|
143
|
-
return_type: :text,
|
|
144
|
-
deterministic: true,
|
|
145
|
-
immutable: true
|
|
146
|
-
)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def execute_scalar(args)
|
|
150
|
-
return nil if args[0].nil?
|
|
151
|
-
args[0].to_s.lstrip
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# RTRIM - Remove trailing whitespace
|
|
156
|
-
class Rtrim < ScalarFunction
|
|
157
|
-
def initialize
|
|
158
|
-
super(:rtrim,
|
|
159
|
-
description: "Remove trailing whitespace",
|
|
160
|
-
category: :string,
|
|
161
|
-
min_args: 1,
|
|
162
|
-
max_args: 1,
|
|
163
|
-
return_type: :text,
|
|
164
|
-
deterministic: true,
|
|
165
|
-
immutable: true
|
|
166
|
-
)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def execute_scalar(args)
|
|
170
|
-
return nil if args[0].nil?
|
|
171
|
-
args[0].to_s.rstrip
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
# REPLACE - Replace substring
|
|
176
|
-
class Replace < ScalarFunction
|
|
177
|
-
def initialize
|
|
178
|
-
super(:replace,
|
|
179
|
-
description: "Replace substring",
|
|
180
|
-
category: :string,
|
|
181
|
-
min_args: 3,
|
|
182
|
-
max_args: 3,
|
|
183
|
-
return_type: :text,
|
|
184
|
-
deterministic: true,
|
|
185
|
-
immutable: true
|
|
186
|
-
)
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
def execute_scalar(args)
|
|
190
|
-
return nil if args[0].nil?
|
|
191
|
-
str = args[0].to_s
|
|
192
|
-
from = args[1].to_s
|
|
193
|
-
to = args[2].to_s
|
|
194
|
-
str.gsub(from, to)
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# POSITION - Find position of substring
|
|
199
|
-
class Position < ScalarFunction
|
|
200
|
-
def initialize
|
|
201
|
-
super(:position,
|
|
202
|
-
description: "Find position of substring",
|
|
203
|
-
category: :string,
|
|
204
|
-
min_args: 2,
|
|
205
|
-
max_args: 2,
|
|
206
|
-
return_type: :integer,
|
|
207
|
-
deterministic: true,
|
|
208
|
-
immutable: true
|
|
209
|
-
)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
def execute_scalar(args)
|
|
213
|
-
return nil if args[0].nil? || args[1].nil?
|
|
214
|
-
substr = args[0].to_s
|
|
215
|
-
str = args[1].to_s
|
|
216
|
-
pos = str.index(substr)
|
|
217
|
-
pos ? pos + 1 : 0
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
# SPLIT_PART - Split string and get part
|
|
222
|
-
class SplitPart < ScalarFunction
|
|
223
|
-
def initialize
|
|
224
|
-
super(:split_part,
|
|
225
|
-
description: "Split string and get part",
|
|
226
|
-
category: :string,
|
|
227
|
-
min_args: 3,
|
|
228
|
-
max_args: 3,
|
|
229
|
-
return_type: :text,
|
|
230
|
-
deterministic: true,
|
|
231
|
-
immutable: true
|
|
232
|
-
)
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def execute_scalar(args)
|
|
236
|
-
return nil if args[0].nil?
|
|
237
|
-
str = args[0].to_s
|
|
238
|
-
delimiter = args[1].to_s
|
|
239
|
-
part = args[2].to_i
|
|
240
|
-
|
|
241
|
-
parts = str.split(delimiter)
|
|
242
|
-
return nil if part <= 0 || part > parts.size
|
|
243
|
-
parts[part - 1]
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
# MD5 - Calculate MD5 hash
|
|
248
|
-
class MD5 < ScalarFunction
|
|
249
|
-
def initialize
|
|
250
|
-
super(:md5,
|
|
251
|
-
description: "Calculate MD5 hash",
|
|
252
|
-
category: :string,
|
|
253
|
-
min_args: 1,
|
|
254
|
-
max_args: 1,
|
|
255
|
-
return_type: :text,
|
|
256
|
-
deterministic: true,
|
|
257
|
-
immutable: true
|
|
258
|
-
)
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
def execute_scalar(args)
|
|
262
|
-
return nil if args[0].nil?
|
|
263
|
-
Digest::MD5.hexdigest(args[0].to_s)
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
# SHA256 - Calculate SHA256 hash
|
|
268
|
-
class SHA256 < ScalarFunction
|
|
269
|
-
def initialize
|
|
270
|
-
super(:sha256,
|
|
271
|
-
description: "Calculate SHA256 hash",
|
|
272
|
-
category: :string,
|
|
273
|
-
min_args: 1,
|
|
274
|
-
max_args: 1,
|
|
275
|
-
return_type: :text,
|
|
276
|
-
deterministic: true,
|
|
277
|
-
immutable: true
|
|
278
|
-
)
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def execute_scalar(args)
|
|
282
|
-
return nil if args[0].nil?
|
|
283
|
-
Digest::SHA256.hexdigest(args[0].to_s)
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
# REVERSE - Reverse string
|
|
288
|
-
class Reverse < ScalarFunction
|
|
289
|
-
def initialize
|
|
290
|
-
super(:reverse,
|
|
291
|
-
description: "Reverse string",
|
|
292
|
-
category: :string,
|
|
293
|
-
min_args: 1,
|
|
294
|
-
max_args: 1,
|
|
295
|
-
return_type: :text,
|
|
296
|
-
deterministic: true,
|
|
297
|
-
immutable: true
|
|
298
|
-
)
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
def execute_scalar(args)
|
|
302
|
-
return nil if args[0].nil?
|
|
303
|
-
args[0].to_s.reverse
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
# LEFT - Get leftmost characters
|
|
308
|
-
class Left < ScalarFunction
|
|
309
|
-
def initialize
|
|
310
|
-
super(:left,
|
|
311
|
-
description: "Get leftmost characters",
|
|
312
|
-
category: :string,
|
|
313
|
-
min_args: 2,
|
|
314
|
-
max_args: 2,
|
|
315
|
-
return_type: :text,
|
|
316
|
-
deterministic: true,
|
|
317
|
-
immutable: true
|
|
318
|
-
)
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
def execute_scalar(args)
|
|
322
|
-
return nil if args[0].nil? || args[1].nil?
|
|
323
|
-
str = args[0].to_s
|
|
324
|
-
count = args[1].to_i
|
|
325
|
-
str[0, count]
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
# RIGHT - Get rightmost characters
|
|
330
|
-
class Right < ScalarFunction
|
|
331
|
-
def initialize
|
|
332
|
-
super(:right,
|
|
333
|
-
description: "Get rightmost characters",
|
|
334
|
-
category: :string,
|
|
335
|
-
min_args: 2,
|
|
336
|
-
max_args: 2,
|
|
337
|
-
return_type: :text,
|
|
338
|
-
deterministic: true,
|
|
339
|
-
immutable: true
|
|
340
|
-
)
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
def execute_scalar(args)
|
|
344
|
-
return nil if args[0].nil? || args[1].nil?
|
|
345
|
-
str = args[0].to_s
|
|
346
|
-
count = args[1].to_i
|
|
347
|
-
str[-count, count] || str
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
# STRING_AGG - String aggregate
|
|
352
|
-
class StringAgg < AggregateFunction
|
|
353
|
-
def initialize
|
|
354
|
-
super(:string_agg,
|
|
355
|
-
description: "Aggregate strings with delimiter",
|
|
356
|
-
category: :string,
|
|
357
|
-
min_args: 2,
|
|
358
|
-
max_args: 2,
|
|
359
|
-
return_type: :text
|
|
360
|
-
)
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
def execute_aggregate(values)
|
|
364
|
-
return nil if values.empty?
|
|
365
|
-
delimiter = values.last
|
|
366
|
-
values = values[0...-1]
|
|
367
|
-
values.compact.join(delimiter.to_s)
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
def combine(state, value)
|
|
371
|
-
return value if state.nil?
|
|
372
|
-
"#{state}#{delimiter}#{value}"
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
private
|
|
376
|
-
|
|
377
|
-
def delimiter
|
|
378
|
-
@delimiter ||= ","
|
|
379
|
-
end
|
|
380
|
-
end
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "base64"
|
|
5
|
+
|
|
6
|
+
module RubyDB
|
|
7
|
+
module Functions
|
|
8
|
+
# StringFunctions - All string-related functions
|
|
9
|
+
class StringFunctions
|
|
10
|
+
# LOWER - Convert string to lowercase
|
|
11
|
+
class Lower < ScalarFunction
|
|
12
|
+
def initialize
|
|
13
|
+
super(:lower,
|
|
14
|
+
description: "Convert string to lowercase",
|
|
15
|
+
category: :string,
|
|
16
|
+
min_args: 1,
|
|
17
|
+
max_args: 1,
|
|
18
|
+
return_type: :text,
|
|
19
|
+
deterministic: true,
|
|
20
|
+
immutable: true
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def execute_scalar(args)
|
|
25
|
+
return nil if args[0].nil?
|
|
26
|
+
args[0].to_s.downcase
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# UPPER - Convert string to uppercase
|
|
31
|
+
class Upper < ScalarFunction
|
|
32
|
+
def initialize
|
|
33
|
+
super(:upper,
|
|
34
|
+
description: "Convert string to uppercase",
|
|
35
|
+
category: :string,
|
|
36
|
+
min_args: 1,
|
|
37
|
+
max_args: 1,
|
|
38
|
+
return_type: :text,
|
|
39
|
+
deterministic: true,
|
|
40
|
+
immutable: true
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def execute_scalar(args)
|
|
45
|
+
return nil if args[0].nil?
|
|
46
|
+
args[0].to_s.upcase
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# LENGTH - Get string length
|
|
51
|
+
class Length < ScalarFunction
|
|
52
|
+
def initialize
|
|
53
|
+
super(:length,
|
|
54
|
+
description: "Get string length",
|
|
55
|
+
category: :string,
|
|
56
|
+
min_args: 1,
|
|
57
|
+
max_args: 1,
|
|
58
|
+
return_type: :integer,
|
|
59
|
+
deterministic: true,
|
|
60
|
+
immutable: true
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def execute_scalar(args)
|
|
65
|
+
return nil if args[0].nil?
|
|
66
|
+
args[0].to_s.length
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# SUBSTR - Extract substring
|
|
71
|
+
class Substr < ScalarFunction
|
|
72
|
+
def initialize
|
|
73
|
+
super(:substr,
|
|
74
|
+
description: "Extract substring",
|
|
75
|
+
category: :string,
|
|
76
|
+
min_args: 2,
|
|
77
|
+
max_args: 3,
|
|
78
|
+
return_type: :text,
|
|
79
|
+
deterministic: true,
|
|
80
|
+
immutable: true
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def execute_scalar(args)
|
|
85
|
+
return nil if args[0].nil? || args[1].nil?
|
|
86
|
+
|
|
87
|
+
str = args[0].to_s
|
|
88
|
+
start_pos = args[1].to_i
|
|
89
|
+
length = args[2] ? args[2].to_i : str.length
|
|
90
|
+
|
|
91
|
+
start_pos -= 1 if start_pos > 0
|
|
92
|
+
str[start_pos, length]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# CONCAT - Concatenate strings
|
|
97
|
+
class Concat < ScalarFunction
|
|
98
|
+
def initialize
|
|
99
|
+
super(:concat,
|
|
100
|
+
description: "Concatenate strings",
|
|
101
|
+
category: :string,
|
|
102
|
+
min_args: 1,
|
|
103
|
+
max_args: -1,
|
|
104
|
+
return_type: :text,
|
|
105
|
+
deterministic: true,
|
|
106
|
+
immutable: true
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def execute_scalar(args)
|
|
111
|
+
args.map { |a| a.to_s }.join
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# TRIM - Remove whitespace
|
|
116
|
+
class Trim < ScalarFunction
|
|
117
|
+
def initialize
|
|
118
|
+
super(:trim,
|
|
119
|
+
description: "Remove leading and trailing whitespace",
|
|
120
|
+
category: :string,
|
|
121
|
+
min_args: 1,
|
|
122
|
+
max_args: 1,
|
|
123
|
+
return_type: :text,
|
|
124
|
+
deterministic: true,
|
|
125
|
+
immutable: true
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def execute_scalar(args)
|
|
130
|
+
return nil if args[0].nil?
|
|
131
|
+
args[0].to_s.strip
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# LTRIM - Remove leading whitespace
|
|
136
|
+
class Ltrim < ScalarFunction
|
|
137
|
+
def initialize
|
|
138
|
+
super(:ltrim,
|
|
139
|
+
description: "Remove leading whitespace",
|
|
140
|
+
category: :string,
|
|
141
|
+
min_args: 1,
|
|
142
|
+
max_args: 1,
|
|
143
|
+
return_type: :text,
|
|
144
|
+
deterministic: true,
|
|
145
|
+
immutable: true
|
|
146
|
+
)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def execute_scalar(args)
|
|
150
|
+
return nil if args[0].nil?
|
|
151
|
+
args[0].to_s.lstrip
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# RTRIM - Remove trailing whitespace
|
|
156
|
+
class Rtrim < ScalarFunction
|
|
157
|
+
def initialize
|
|
158
|
+
super(:rtrim,
|
|
159
|
+
description: "Remove trailing whitespace",
|
|
160
|
+
category: :string,
|
|
161
|
+
min_args: 1,
|
|
162
|
+
max_args: 1,
|
|
163
|
+
return_type: :text,
|
|
164
|
+
deterministic: true,
|
|
165
|
+
immutable: true
|
|
166
|
+
)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def execute_scalar(args)
|
|
170
|
+
return nil if args[0].nil?
|
|
171
|
+
args[0].to_s.rstrip
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# REPLACE - Replace substring
|
|
176
|
+
class Replace < ScalarFunction
|
|
177
|
+
def initialize
|
|
178
|
+
super(:replace,
|
|
179
|
+
description: "Replace substring",
|
|
180
|
+
category: :string,
|
|
181
|
+
min_args: 3,
|
|
182
|
+
max_args: 3,
|
|
183
|
+
return_type: :text,
|
|
184
|
+
deterministic: true,
|
|
185
|
+
immutable: true
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def execute_scalar(args)
|
|
190
|
+
return nil if args[0].nil?
|
|
191
|
+
str = args[0].to_s
|
|
192
|
+
from = args[1].to_s
|
|
193
|
+
to = args[2].to_s
|
|
194
|
+
str.gsub(from, to)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# POSITION - Find position of substring
|
|
199
|
+
class Position < ScalarFunction
|
|
200
|
+
def initialize
|
|
201
|
+
super(:position,
|
|
202
|
+
description: "Find position of substring",
|
|
203
|
+
category: :string,
|
|
204
|
+
min_args: 2,
|
|
205
|
+
max_args: 2,
|
|
206
|
+
return_type: :integer,
|
|
207
|
+
deterministic: true,
|
|
208
|
+
immutable: true
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def execute_scalar(args)
|
|
213
|
+
return nil if args[0].nil? || args[1].nil?
|
|
214
|
+
substr = args[0].to_s
|
|
215
|
+
str = args[1].to_s
|
|
216
|
+
pos = str.index(substr)
|
|
217
|
+
pos ? pos + 1 : 0
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# SPLIT_PART - Split string and get part
|
|
222
|
+
class SplitPart < ScalarFunction
|
|
223
|
+
def initialize
|
|
224
|
+
super(:split_part,
|
|
225
|
+
description: "Split string and get part",
|
|
226
|
+
category: :string,
|
|
227
|
+
min_args: 3,
|
|
228
|
+
max_args: 3,
|
|
229
|
+
return_type: :text,
|
|
230
|
+
deterministic: true,
|
|
231
|
+
immutable: true
|
|
232
|
+
)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def execute_scalar(args)
|
|
236
|
+
return nil if args[0].nil?
|
|
237
|
+
str = args[0].to_s
|
|
238
|
+
delimiter = args[1].to_s
|
|
239
|
+
part = args[2].to_i
|
|
240
|
+
|
|
241
|
+
parts = str.split(delimiter)
|
|
242
|
+
return nil if part <= 0 || part > parts.size
|
|
243
|
+
parts[part - 1]
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# MD5 - Calculate MD5 hash
|
|
248
|
+
class MD5 < ScalarFunction
|
|
249
|
+
def initialize
|
|
250
|
+
super(:md5,
|
|
251
|
+
description: "Calculate MD5 hash",
|
|
252
|
+
category: :string,
|
|
253
|
+
min_args: 1,
|
|
254
|
+
max_args: 1,
|
|
255
|
+
return_type: :text,
|
|
256
|
+
deterministic: true,
|
|
257
|
+
immutable: true
|
|
258
|
+
)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def execute_scalar(args)
|
|
262
|
+
return nil if args[0].nil?
|
|
263
|
+
Digest::MD5.hexdigest(args[0].to_s)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# SHA256 - Calculate SHA256 hash
|
|
268
|
+
class SHA256 < ScalarFunction
|
|
269
|
+
def initialize
|
|
270
|
+
super(:sha256,
|
|
271
|
+
description: "Calculate SHA256 hash",
|
|
272
|
+
category: :string,
|
|
273
|
+
min_args: 1,
|
|
274
|
+
max_args: 1,
|
|
275
|
+
return_type: :text,
|
|
276
|
+
deterministic: true,
|
|
277
|
+
immutable: true
|
|
278
|
+
)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def execute_scalar(args)
|
|
282
|
+
return nil if args[0].nil?
|
|
283
|
+
Digest::SHA256.hexdigest(args[0].to_s)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# REVERSE - Reverse string
|
|
288
|
+
class Reverse < ScalarFunction
|
|
289
|
+
def initialize
|
|
290
|
+
super(:reverse,
|
|
291
|
+
description: "Reverse string",
|
|
292
|
+
category: :string,
|
|
293
|
+
min_args: 1,
|
|
294
|
+
max_args: 1,
|
|
295
|
+
return_type: :text,
|
|
296
|
+
deterministic: true,
|
|
297
|
+
immutable: true
|
|
298
|
+
)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def execute_scalar(args)
|
|
302
|
+
return nil if args[0].nil?
|
|
303
|
+
args[0].to_s.reverse
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# LEFT - Get leftmost characters
|
|
308
|
+
class Left < ScalarFunction
|
|
309
|
+
def initialize
|
|
310
|
+
super(:left,
|
|
311
|
+
description: "Get leftmost characters",
|
|
312
|
+
category: :string,
|
|
313
|
+
min_args: 2,
|
|
314
|
+
max_args: 2,
|
|
315
|
+
return_type: :text,
|
|
316
|
+
deterministic: true,
|
|
317
|
+
immutable: true
|
|
318
|
+
)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def execute_scalar(args)
|
|
322
|
+
return nil if args[0].nil? || args[1].nil?
|
|
323
|
+
str = args[0].to_s
|
|
324
|
+
count = args[1].to_i
|
|
325
|
+
str[0, count]
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# RIGHT - Get rightmost characters
|
|
330
|
+
class Right < ScalarFunction
|
|
331
|
+
def initialize
|
|
332
|
+
super(:right,
|
|
333
|
+
description: "Get rightmost characters",
|
|
334
|
+
category: :string,
|
|
335
|
+
min_args: 2,
|
|
336
|
+
max_args: 2,
|
|
337
|
+
return_type: :text,
|
|
338
|
+
deterministic: true,
|
|
339
|
+
immutable: true
|
|
340
|
+
)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def execute_scalar(args)
|
|
344
|
+
return nil if args[0].nil? || args[1].nil?
|
|
345
|
+
str = args[0].to_s
|
|
346
|
+
count = args[1].to_i
|
|
347
|
+
str[-count, count] || str
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# STRING_AGG - String aggregate
|
|
352
|
+
class StringAgg < AggregateFunction
|
|
353
|
+
def initialize
|
|
354
|
+
super(:string_agg,
|
|
355
|
+
description: "Aggregate strings with delimiter",
|
|
356
|
+
category: :string,
|
|
357
|
+
min_args: 2,
|
|
358
|
+
max_args: 2,
|
|
359
|
+
return_type: :text
|
|
360
|
+
)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def execute_aggregate(values)
|
|
364
|
+
return nil if values.empty?
|
|
365
|
+
delimiter = values.last
|
|
366
|
+
values = values[0...-1]
|
|
367
|
+
values.compact.join(delimiter.to_s)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def combine(state, value)
|
|
371
|
+
return value if state.nil?
|
|
372
|
+
"#{state}#{delimiter}#{value}"
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
private
|
|
376
|
+
|
|
377
|
+
def delimiter
|
|
378
|
+
@delimiter ||= ","
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|