Skip to content

Commit 1a11dd5

Browse files
author
Anton Schieber
committed
src: generator: hypertable: edition support
Add support for the apache and community editions of timescaledb in the hypertable generator.
1 parent c106b10 commit 1a11dd5

1 file changed

Lines changed: 73 additions & 25 deletions

File tree

src/Eftdb/Generators/HypertableOperationGenerator.cs

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using CmdScale.EntityFrameworkCore.TimescaleDB.Abstractions;
22
using CmdScale.EntityFrameworkCore.TimescaleDB.Operations;
3+
using System.Text;
34

45
namespace CmdScale.EntityFrameworkCore.TimescaleDB.Generators
56
{
@@ -16,54 +17,57 @@ public HypertableOperationGenerator(bool isDesignTime = false)
1617
}
1718

1819
sqlHelper = new SqlBuilderHelper(quoteString);
19-
2020
}
2121

2222
public List<string> Generate(CreateHypertableOperation operation)
2323
{
2424
string qualifiedTableName = sqlHelper.Regclass(operation.TableName, operation.Schema);
2525
string qualifiedIdentifier = sqlHelper.QualifiedIdentifier(operation.TableName, operation.Schema);
2626

27-
List<string> statements =
28-
[
29-
$"SELECT create_hypertable({qualifiedTableName}, '{operation.TimeColumnName}');"
30-
];
27+
List<string> statements = [];
28+
List<string> communityStatements = [];
3129

32-
// ChunkTimeInterval
30+
// Build create_hypertable with chunk_time_interval if provided
31+
var createHypertableCall = new StringBuilder();
32+
createHypertableCall.Append($"SELECT create_hypertable({qualifiedTableName}, '{operation.TimeColumnName}'");
33+
3334
if (!string.IsNullOrEmpty(operation.ChunkTimeInterval))
3435
{
3536
// Check if the interval is a plain number (e.g., for microseconds).
3637
if (long.TryParse(operation.ChunkTimeInterval, out _))
3738
{
3839
// If it's a number, don't wrap it in quotes.
39-
statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, {operation.ChunkTimeInterval}::bigint);");
40+
createHypertableCall.Append($", chunk_time_interval => {operation.ChunkTimeInterval}::bigint");
4041
}
4142
else
4243
{
4344
// If it's a string like '7 days', wrap it in quotes.
44-
statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, INTERVAL '{operation.ChunkTimeInterval}');");
45+
createHypertableCall.Append($", chunk_time_interval => INTERVAL '{operation.ChunkTimeInterval}'");
4546
}
4647
}
48+
49+
createHypertableCall.Append(");");
50+
statements.Add(createHypertableCall.ToString());
4751

48-
// EnableCompression
52+
// EnableCompression (Community Edition only)
4953
if (operation.EnableCompression || operation.ChunkSkipColumns?.Count > 0)
5054
{
5155
bool enableCompression = operation.EnableCompression || operation.ChunkSkipColumns != null && operation.ChunkSkipColumns.Count > 0;
52-
statements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {enableCompression.ToString().ToLower()});");
56+
communityStatements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {enableCompression.ToString().ToLower()});");
5357
}
5458

55-
// ChunkSkipColumns
59+
// ChunkSkipColumns (Community Edition only)
5660
if (operation.ChunkSkipColumns != null && operation.ChunkSkipColumns.Count > 0)
5761
{
58-
statements.Add("SET timescaledb.enable_chunk_skipping = 'ON';");
62+
communityStatements.Add("SET timescaledb.enable_chunk_skipping = 'ON';");
5963

6064
foreach (string column in operation.ChunkSkipColumns)
6165
{
62-
statements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');");
66+
communityStatements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');");
6367
}
6468
}
6569

66-
// AdditionalDimensions
70+
// AdditionalDimensions (Available in both editions)
6771
if (operation.AdditionalDimensions != null && operation.AdditionalDimensions.Count > 0)
6872
{
6973
foreach (Dimension dimension in operation.AdditionalDimensions)
@@ -85,6 +89,11 @@ public List<string> Generate(CreateHypertableOperation operation)
8589
}
8690
}
8791

92+
// Add wrapped community statements if any exist
93+
if (communityStatements.Count > 0)
94+
{
95+
statements.Add(WrapCommunityFeatures(communityStatements));
96+
}
8897
return statements;
8998
}
9099

@@ -94,45 +103,52 @@ public List<string> Generate(AlterHypertableOperation operation)
94103
string qualifiedIdentifier = sqlHelper.QualifiedIdentifier(operation.TableName, operation.Schema);
95104

96105
List<string> statements = [];
106+
List<string> communityStatements = [];
97107

98-
// Check for ChunkTimeInterval change
108+
// Check for ChunkTimeInterval change (Available in both editions)
99109
if (operation.ChunkTimeInterval != operation.OldChunkTimeInterval)
100110
{
111+
var setChunkTimeInterval = new StringBuilder();
112+
setChunkTimeInterval.Append($"SELECT set_chunk_time_interval({qualifiedTableName}, ");
113+
101114
// Check if the interval is a plain number (e.g., for microseconds).
102115
if (long.TryParse(operation.ChunkTimeInterval, out _))
103116
{
104117
// If it's a number, don't wrap it in quotes.
105-
statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, {operation.ChunkTimeInterval}::bigint);");
118+
setChunkTimeInterval.Append($"{operation.ChunkTimeInterval}::bigint");
106119
}
107120
else
108121
{
109122
// If it's a string like '7 days', wrap it in quotes.
110-
statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, INTERVAL '{operation.ChunkTimeInterval}');");
123+
setChunkTimeInterval.Append($"INTERVAL '{operation.ChunkTimeInterval}'");
111124
}
125+
126+
setChunkTimeInterval.Append(");");
127+
statements.Add(setChunkTimeInterval.ToString());
112128
}
113129

114-
// Check for EnableCompression change
130+
// Check for EnableCompression change (Community Edition only)
115131
bool newCompressionState = operation.EnableCompression || operation.ChunkSkipColumns != null && operation.ChunkSkipColumns.Any();
116132
bool oldCompressionState = operation.OldEnableCompression || operation.OldChunkSkipColumns != null && operation.OldChunkSkipColumns.Any();
117133

118134
if (newCompressionState != oldCompressionState)
119135
{
120136
string compressionValue = newCompressionState.ToString().ToLower();
121-
statements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {compressionValue});");
137+
communityStatements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {compressionValue});");
122138
}
123139

124-
// Handle ChunkSkipColumns
140+
// Handle ChunkSkipColumns (Community Edition only)
125141
IReadOnlyList<string> newColumns = operation.ChunkSkipColumns ?? [];
126142
IReadOnlyList<string> oldColumns = operation.OldChunkSkipColumns ?? [];
127143
List<string> addedColumns = [.. newColumns.Except(oldColumns)];
128144

129145
if (addedColumns.Count != 0)
130146
{
131-
statements.Add("SET timescaledb.enable_chunk_skipping = 'ON';");
147+
communityStatements.Add("SET timescaledb.enable_chunk_skipping = 'ON';");
132148

133149
foreach (string column in addedColumns)
134150
{
135-
statements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');");
151+
communityStatements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');");
136152
}
137153
}
138154

@@ -141,7 +157,7 @@ public List<string> Generate(AlterHypertableOperation operation)
141157
{
142158
foreach (string column in removedColumns)
143159
{
144-
statements.Add($"SELECT disable_chunk_skipping({qualifiedTableName}, '{column}');");
160+
communityStatements.Add($"SELECT disable_chunk_skipping({qualifiedTableName}, '{column}');");
145161
}
146162
}
147163

@@ -192,8 +208,40 @@ public List<string> Generate(AlterHypertableOperation operation)
192208
statements.Add($"-- WARNING: TimescaleDB does not support removing dimensions. The following dimensions cannot be removed: {dimensionList}");
193209
}
194210

211+
212+
// Add wrapped community statements if any exist
213+
if (communityStatements.Count > 0)
214+
{
215+
statements.Add(WrapCommunityFeatures(communityStatements));
216+
}
195217
return statements;
196218
}
197-
}
198-
}
199219

220+
/// <summary>
221+
/// Wraps multiple SQL statements in a single license check block to ensure they only run on Community Edition
222+
/// </summary>
223+
private string WrapCommunityFeatures(List<string> sqlStatements)
224+
{
225+
var sb = new StringBuilder();
226+
sb.AppendLine("DO $$");
227+
sb.AppendLine("DECLARE");
228+
sb.AppendLine(" license TEXT;");
229+
sb.AppendLine("BEGIN");
230+
sb.AppendLine(" license := current_setting('timescaledb.license', true);");
231+
sb.AppendLine(" ");
232+
sb.AppendLine(" IF license IS NULL OR license != 'apache' THEN");
233+
234+
foreach (string sql in sqlStatements)
235+
{
236+
sb.AppendLine($" {sql}");
237+
}
238+
239+
sb.AppendLine(" ELSE");
240+
sb.AppendLine(" RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Enterprise Edition';");
241+
sb.AppendLine(" END IF;");
242+
sb.AppendLine("END $$;");
243+
244+
return sb.ToString();
245+
}
246+
}
247+
}

0 commit comments

Comments
 (0)