Skip to content

Commit 77c4727

Browse files
authored
Merge pull request #4 from quackscience/HSCAN
HSCAN support
2 parents 578e9cc + 8dfab48 commit 77c4727

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

src/redis_extension.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ class RedisProtocol {
101101
return cmd;
102102
}
103103

104+
static std::string formatHScan(const std::string& key, const std::string& cursor, const std::string& pattern = "*", int64_t count = 10) {
105+
std::string cmd = "*6\r\n$5\r\nHSCAN\r\n";
106+
cmd += "$" + std::to_string(key.length()) + "\r\n" + key + "\r\n";
107+
cmd += "$" + std::to_string(cursor.length()) + "\r\n" + cursor + "\r\n";
108+
cmd += "$5\r\nMATCH\r\n";
109+
cmd += "$" + std::to_string(pattern.length()) + "\r\n" + pattern + "\r\n";
110+
cmd += "$5\r\nCOUNT\r\n";
111+
auto count_str = std::to_string(count);
112+
cmd += "$" + std::to_string(count_str.length()) + "\r\n" + count_str + "\r\n";
113+
return cmd;
114+
}
115+
104116
static std::vector<std::string> parseArrayResponse(const std::string& response) {
105117
std::vector<std::string> result;
106118
if (response.empty() || response[0] != '*') return result;
@@ -489,6 +501,53 @@ static void RedisScanFunction(DataChunk &args, ExpressionState &state, Vector &r
489501
});
490502
}
491503

504+
static void RedisHScanFunction(DataChunk &args, ExpressionState &state, Vector &result) {
505+
auto &key_vector = args.data[0];
506+
auto &cursor_vector = args.data[1];
507+
auto &pattern_vector = args.data[2];
508+
auto &count_vector = args.data[3];
509+
auto &secret_vector = args.data[4];
510+
511+
BinaryExecutor::Execute<string_t, string_t, string_t>(
512+
key_vector, cursor_vector, result, args.size(),
513+
[&](string_t key, string_t cursor) {
514+
try {
515+
string host, port, password;
516+
if (!GetRedisSecret(state.GetContext(), secret_vector.GetValue(0).ToString(),
517+
host, port, password)) {
518+
throw InvalidInputException("Redis secret not found");
519+
}
520+
521+
auto pattern = pattern_vector.GetValue(0).ToString();
522+
auto count = count_vector.GetValue(0).GetValue<int64_t>();
523+
auto conn = ConnectionPool::getInstance().getConnection(host, port, password);
524+
auto response = conn->execute(RedisProtocol::formatHScan(
525+
key.GetString(),
526+
cursor.GetString(),
527+
pattern,
528+
count
529+
));
530+
531+
// HSCAN returns [cursor, [field1, value1, field2, value2, ...]]
532+
auto scan_result = RedisProtocol::parseArrayResponse(response);
533+
std::string result_str;
534+
if (scan_result.size() >= 2) {
535+
result_str = scan_result[0] + ":";
536+
auto kvs = RedisProtocol::parseArrayResponse(scan_result[1]);
537+
for (size_t i = 0; i < kvs.size(); i += 2) {
538+
if (i > 0) result_str += ",";
539+
result_str += kvs[i] + "=" + ((i + 1) < kvs.size() ? kvs[i + 1] : "");
540+
}
541+
} else {
542+
result_str = "0:";
543+
}
544+
return StringVector::AddString(result, result_str);
545+
} catch (std::exception &e) {
546+
throw InvalidInputException("Redis HSCAN error: %s", e.what());
547+
}
548+
});
549+
}
550+
492551
static void LoadInternal(DatabaseInstance &instance) {
493552
// Register the secret functions first!
494553
CreateRedisSecretFunctions::Register(instance);
@@ -581,6 +640,21 @@ static void LoadInternal(DatabaseInstance &instance) {
581640
RedisScanFunction
582641
);
583642
ExtensionUtil::RegisterFunction(instance, redis_scan_func);
643+
644+
// Register HSCAN
645+
auto redis_hscan_func = ScalarFunction(
646+
"redis_hscan",
647+
{
648+
LogicalType::VARCHAR, // key
649+
LogicalType::VARCHAR, // cursor
650+
LogicalType::VARCHAR, // pattern
651+
LogicalType::BIGINT, // count
652+
LogicalType::VARCHAR // secret_name
653+
},
654+
LogicalType::VARCHAR,
655+
RedisHScanFunction
656+
);
657+
ExtensionUtil::RegisterFunction(instance, redis_hscan_func);
584658
}
585659

586660
void RedisExtension::Load(DuckDB &db) {

0 commit comments

Comments
 (0)