|
1 |
| -# DuckDB Redis Extension |
2 |
| -This extension provides Redis client functionality for DuckDB, allowing you to interact with a Redis server directly from SQL queries. The extension uses Boost.Asio for network communication and implements basic Redis protocol commands. |
| 1 | +<img src="https://github.com/user-attachments/assets/46a5c546-7e9b-42c7-87f4-bc8defe674e0" width=250 /> |
| 2 | + |
| 3 | +# DuckDB Redis Client Extension |
| 4 | +This extension provides Redis client functionality for DuckDB, allowing you to interact with a Redis server directly from SQL queries. |
| 5 | + |
| 6 | +> Experimental: USE AT YOUR OWN RISK! |
3 | 7 |
|
4 | 8 | ## Features
|
5 | 9 | Currently supported Redis operations:
|
6 |
| -- `redis_get(key, host, port, password)`: Retrieves a value from Redis for a given key |
7 |
| -- `redis_set(key, value, host, port, password)`: Sets a value in Redis for a given key |
8 |
| - |
9 |
| -Features: |
10 |
| -- Connection pooling for improved performance |
11 |
| -- Redis authentication support |
12 |
| -- Thread-safe operations |
13 |
| -- Detailed error handling |
| 10 | +- String operations: `GET`, `SET` |
| 11 | +- Hash operations: `HGET`, `HSET` |
| 12 | +- List operations: `LPUSH`, `LRANGE` |
| 13 | +- Batch operations: `MGET`, `SCAN` |
14 | 14 |
|
15 | 15 | ## Installation
|
16 | 16 | ```sql
|
17 |
| -INSTALL 'redis' FROM community; |
18 |
| -LOAD 'redis'; |
| 17 | +INSTALL redis FROM community; |
| 18 | +LOAD redis; |
19 | 19 | ```
|
20 | 20 |
|
21 |
| -## Usage Examples |
22 |
| -### Connecting with Authentication |
| 21 | +## Usage |
| 22 | +### Setting up Redis Connection |
| 23 | +First, create a secret to store your Redis connection details: |
23 | 24 | ```sql
|
24 |
| --- Set a value with authentication |
25 |
| -SELECT redis_set('user:1', 'John Doe', 'localhost', '6379', 'mypassword') as result; |
26 |
| - |
27 |
| --- Get a value with authentication |
28 |
| -SELECT redis_get('user:1', 'localhost', '6379', 'mypassword') as user_name; |
29 |
| - |
30 |
| --- For non-authenticated Redis servers, pass an empty string as password |
31 |
| -SELECT redis_get('user:1', 'localhost', '6379', '') as user_name; |
| 25 | +-- Create a Redis connection secret |
| 26 | +CALL redis_create_secret('my_redis', { |
| 27 | + 'host': 'localhost', |
| 28 | + 'port': '6379', |
| 29 | + 'password': 'optional_password' |
| 30 | +}); |
| 31 | + |
| 32 | +-- For cloud Redis services (e.g., Redis Labs) |
| 33 | +CALL redis_create_secret('redis_cloud', { |
| 34 | + 'host': 'redis-xxxxx.cloud.redislabs.com', |
| 35 | + 'port': '16379', |
| 36 | + 'password': 'your_password' |
| 37 | +}); |
32 | 38 | ```
|
33 | 39 |
|
34 |
| -### Setting Values in Redis |
| 40 | +### String Operations |
35 | 41 | ```sql
|
36 |
| --- Set a single value |
37 |
| -SELECT redis_set('user:1', 'John Doe', 'localhost', '6379') as result; |
| 42 | +-- Set a value |
| 43 | +SELECT redis_set('user:1', 'John Doe', 'my_redis') as result; |
| 44 | + |
| 45 | +-- Get a value |
| 46 | +SELECT redis_get('user:1', 'my_redis') as user_name; |
38 | 47 |
|
39 | 48 | -- Set multiple values in a query
|
40 |
| -INSERT INTO users (id, name, age) |
41 |
| -SELECT redis_set( |
| 49 | +INSERT INTO users (id, name) |
| 50 | +SELECT id, redis_set( |
42 | 51 | 'user:' || id::VARCHAR,
|
43 | 52 | name,
|
44 |
| - 'localhost', |
45 |
| - '6379' |
| 53 | + 'my_redis' |
46 | 54 | )
|
47 | 55 | FROM new_users;
|
48 | 56 | ```
|
49 | 57 |
|
50 |
| -### Getting Values from Redis |
| 58 | +### Hash Operations |
| 59 | +```sql |
| 60 | +-- Set hash fields |
| 61 | +SELECT redis_hset( 'user:1', 'email', '[email protected]', 'my_redis'); |
| 62 | +SELECT redis_hset('user:1', 'age', '30', 'my_redis'); |
| 63 | + |
| 64 | +-- Get hash field |
| 65 | +SELECT redis_hget('user:1', 'email', 'my_redis') as email; |
| 66 | + |
| 67 | +-- Store user profile as hash |
| 68 | +WITH profile(id, field, value) AS ( |
| 69 | + VALUES |
| 70 | + (1, 'name', 'John Doe'), |
| 71 | + ( 1, 'email', '[email protected]'), |
| 72 | + (1, 'age', '30') |
| 73 | +) |
| 74 | +SELECT redis_hset( |
| 75 | + 'user:' || id::VARCHAR, |
| 76 | + field, |
| 77 | + value, |
| 78 | + 'my_redis' |
| 79 | +) |
| 80 | +FROM profile; |
| 81 | +``` |
| 82 | + |
| 83 | +### List Operations |
51 | 84 | ```sql
|
52 |
| --- Get a single value |
53 |
| -SELECT redis_get('user:1', 'localhost', '6379') as user_name; |
54 |
| - |
55 |
| --- Get multiple values |
56 |
| -SELECT |
57 |
| - id, |
58 |
| - redis_get('user:' || id::VARCHAR, 'localhost', '6379') as user_data |
59 |
| -FROM user_ids; |
| 85 | +-- Push items to list |
| 86 | +SELECT redis_lpush('mylist', 'first_item', 'my_redis'); |
| 87 | +SELECT redis_lpush('mylist', 'second_item', 'my_redis'); |
| 88 | + |
| 89 | +-- Get range from list (returns comma-separated values) |
| 90 | +-- Get all items (0 to -1 means start to end) |
| 91 | +SELECT redis_lrange('mylist', 0, -1, 'my_redis') as items; |
| 92 | + |
| 93 | +-- Get first 5 items |
| 94 | +SELECT redis_lrange('mylist', 0, 4, 'my_redis') as items; |
| 95 | + |
| 96 | +-- Push multiple items |
| 97 | +WITH items(value) AS ( |
| 98 | + VALUES ('item1'), ('item2'), ('item3') |
| 99 | +) |
| 100 | +SELECT redis_lpush('mylist', value, 'my_redis') |
| 101 | +FROM items; |
60 | 102 | ```
|
61 | 103 |
|
62 | 104 | ### Batch Operations
|
63 | 105 | ```sql
|
64 | 106 | -- Get multiple keys at once
|
65 |
| -SELECT redis_mget('key1,key2,key3', 'localhost', '6379', '') as values; |
| 107 | +SELECT redis_mget('key1,key2,key3', 'my_redis') as values; |
66 | 108 |
|
67 | 109 | -- Scan keys matching a pattern
|
68 |
| -SELECT redis_scan('0', 'user:*', 10, 'localhost', '6379', '') as result; |
| 110 | +SELECT redis_scan('0', 'user:*', 10, 'my_redis') as result; |
69 | 111 | -- Returns: "cursor:key1,key2,key3" where cursor is the next position for scanning
|
70 | 112 | -- Use the returned cursor for the next scan until cursor is 0
|
71 | 113 |
|
72 | 114 | -- Scan all keys matching a pattern
|
73 | 115 | WITH RECURSIVE scan(cursor, keys) AS (
|
74 | 116 | -- Initial scan
|
75 |
| - SELECT split_part(redis_scan('0', 'user:*', 10, 'localhost', '6379', ''), ':', 1), |
76 |
| - split_part(redis_scan('0', 'user:*', 10, 'localhost', '6379', ''), ':', 2) |
| 117 | + SELECT split_part(redis_scan('0', 'user:*', 10, 'my_redis'), ':', 1), |
| 118 | + split_part(redis_scan('0', 'user:*', 10, 'my_redis'), ':', 2) |
77 | 119 | UNION ALL
|
78 | 120 | -- Continue scanning until cursor is 0
|
79 |
| - SELECT split_part(redis_scan(cursor, 'user:*', 10, 'localhost', '6379', ''), ':', 1), |
80 |
| - split_part(redis_scan(cursor, 'user:*', 10, 'localhost', '6379', ''), ':', 2) |
| 121 | + SELECT split_part(redis_scan(cursor, 'user:*', 10, 'my_redis'), ':', 1), |
| 122 | + split_part(redis_scan(cursor, 'user:*', 10, 'my_redis'), ':', 2) |
81 | 123 | FROM scan
|
82 | 124 | WHERE cursor != '0'
|
83 | 125 | )
|
84 | 126 | SELECT keys FROM scan;
|
85 | 127 | ```
|
86 | 128 |
|
| 129 | +## Error Handling |
| 130 | +The extension functions will throw exceptions with descriptive error messages when: |
| 131 | +- Redis secret is not found or invalid |
| 132 | +- Unable to connect to Redis server |
| 133 | +- Network communication errors occur |
| 134 | +- Invalid Redis protocol responses are received |
| 135 | + |
87 | 136 | ## Building from Source
|
88 | 137 | Follow the standard DuckDB extension build process:
|
89 | 138 |
|
|
98 | 147 | ## Dependencies
|
99 | 148 | - Boost.Asio (header-only, installed via vcpkg)
|
100 | 149 |
|
101 |
| -## Error Handling |
102 |
| -The extension functions will throw exceptions with descriptive error messages when: |
103 |
| -- Unable to connect to Redis server |
104 |
| -- Network communication errors occur |
105 |
| -- Invalid Redis protocol responses are received |
106 |
| - |
107 | 150 | ## Future Enhancements
|
108 | 151 | Planned features include:
|
109 |
| -- Support for Redis authentication |
110 |
| -- Connection pooling for better performance |
111 |
| -- Additional Redis commands (HGET, HSET, LPUSH, etc.) |
112 |
| -- Table functions for scanning Redis keys |
| 152 | +- Additional Redis commands (SADD, SMEMBERS, etc.) |
113 | 153 | - Batch operations using Redis pipelines
|
114 | 154 | - Connection timeout handling
|
115 | 155 |
|
|
0 commit comments