Skip to content

Commit 4ab179b

Browse files
src: generator: hypertable: edition support
Add support for the apache and community editions of timescaledb in the hypertable generator.
1 parent 238114a commit 4ab179b

File tree

1 file changed

+73
-25
lines changed

1 file changed

+73
-25
lines changed

CmdScale.EntityFrameworkCore.TimescaleDB/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)
@@ -79,6 +83,11 @@ public List<string> Generate(CreateHypertableOperation operation)
7983
}
8084
}
8185

86+
// Add wrapped community statements if any exist
87+
if (communityStatements.Count > 0)
88+
{
89+
statements.Add(WrapCommunityFeatures(communityStatements));
90+
}
8291
return statements;
8392
}
8493

@@ -88,45 +97,52 @@ public List<string> Generate(AlterHypertableOperation operation)
8897
string qualifiedIdentifier = sqlHelper.QualifiedIdentifier(operation.TableName, operation.Schema);
8998

9099
List<string> statements = [];
100+
List<string> communityStatements = [];
91101

92-
// Check for ChunkTimeInterval change
102+
// Check for ChunkTimeInterval change (Available in both editions)
93103
if (operation.ChunkTimeInterval != operation.OldChunkTimeInterval)
94104
{
105+
var setChunkTimeInterval = new StringBuilder();
106+
setChunkTimeInterval.Append($"SELECT set_chunk_time_interval({qualifiedTableName}, ");
107+
95108
// Check if the interval is a plain number (e.g., for microseconds).
96109
if (long.TryParse(operation.ChunkTimeInterval, out _))
97110
{
98111
// If it's a number, don't wrap it in quotes.
99-
statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, {operation.ChunkTimeInterval}::bigint);");
112+
setChunkTimeInterval.Append($"{operation.ChunkTimeInterval}::bigint");
100113
}
101114
else
102115
{
103116
// If it's a string like '7 days', wrap it in quotes.
104-
statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, INTERVAL '{operation.ChunkTimeInterval}');");
117+
setChunkTimeInterval.Append($"INTERVAL '{operation.ChunkTimeInterval}'");
105118
}
119+
120+
setChunkTimeInterval.Append(");");
121+
statements.Add(setChunkTimeInterval.ToString());
106122
}
107123

108-
// Check for EnableCompression change
124+
// Check for EnableCompression change (Community Edition only)
109125
bool newCompressionState = operation.EnableCompression || operation.ChunkSkipColumns != null && operation.ChunkSkipColumns.Any();
110126
bool oldCompressionState = operation.OldEnableCompression || operation.OldChunkSkipColumns != null && operation.OldChunkSkipColumns.Any();
111127

112128
if (newCompressionState != oldCompressionState)
113129
{
114130
string compressionValue = newCompressionState.ToString().ToLower();
115-
statements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {compressionValue});");
131+
communityStatements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {compressionValue});");
116132
}
117133

118-
// Handle ChunkSkipColumns
134+
// Handle ChunkSkipColumns (Community Edition only)
119135
IReadOnlyList<string> newColumns = operation.ChunkSkipColumns ?? [];
120136
IReadOnlyList<string> oldColumns = operation.OldChunkSkipColumns ?? [];
121137
List<string> addedColumns = [.. newColumns.Except(oldColumns)];
122138

123139
if (addedColumns.Count != 0)
124140
{
125-
statements.Add("SET timescaledb.enable_chunk_skipping = 'ON';");
141+
communityStatements.Add("SET timescaledb.enable_chunk_skipping = 'ON';");
126142

127143
foreach (string column in addedColumns)
128144
{
129-
statements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');");
145+
communityStatements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');");
130146
}
131147
}
132148

@@ -135,7 +151,7 @@ public List<string> Generate(AlterHypertableOperation operation)
135151
{
136152
foreach (string column in removedColumns)
137153
{
138-
statements.Add($"SELECT disable_chunk_skipping({qualifiedTableName}, '{column}');");
154+
communityStatements.Add($"SELECT disable_chunk_skipping({qualifiedTableName}, '{column}');");
139155
}
140156
}
141157

@@ -180,8 +196,40 @@ public List<string> Generate(AlterHypertableOperation operation)
180196
statements.Add($"-- WARNING: TimescaleDB does not support removing dimensions. The following dimensions cannot be removed: {dimensionList}");
181197
}
182198

199+
200+
// Add wrapped community statements if any exist
201+
if (communityStatements.Count > 0)
202+
{
203+
statements.Add(WrapCommunityFeatures(communityStatements));
204+
}
183205
return statements;
184206
}
185-
}
186-
}
187207

208+
/// <summary>
209+
/// Wraps multiple SQL statements in a single license check block to ensure they only run on Community Edition
210+
/// </summary>
211+
private string WrapCommunityFeatures(List<string> sqlStatements)
212+
{
213+
var sb = new StringBuilder();
214+
sb.AppendLine("DO $$");
215+
sb.AppendLine("DECLARE");
216+
sb.AppendLine(" license TEXT;");
217+
sb.AppendLine("BEGIN");
218+
sb.AppendLine(" license := current_setting('timescaledb.license', true);");
219+
sb.AppendLine(" ");
220+
sb.AppendLine(" IF license IS NULL OR license != 'apache' THEN");
221+
222+
foreach (string sql in sqlStatements)
223+
{
224+
sb.AppendLine($" {sql}");
225+
}
226+
227+
sb.AppendLine(" ELSE");
228+
sb.AppendLine(" RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Enterprise Edition';");
229+
sb.AppendLine(" END IF;");
230+
sb.AppendLine("END $$;");
231+
232+
return sb.ToString();
233+
}
234+
}
235+
}

0 commit comments

Comments
 (0)