diff --git a/benchmarks/Eftdb.Benchmarks/WriteRecordsBenchmarkBase.cs b/benchmarks/Eftdb.Benchmarks/WriteRecordsBenchmarkBase.cs index 4a3d0d8..ce5e85d 100644 --- a/benchmarks/Eftdb.Benchmarks/WriteRecordsBenchmarkBase.cs +++ b/benchmarks/Eftdb.Benchmarks/WriteRecordsBenchmarkBase.cs @@ -46,13 +46,13 @@ public async Task GlobalCleanup() public void IterationSetup() { Trades.Clear(); - var random = new Random(); + Random random = new(); string[] tickers = ["AAPL", "GOOGL", "MSFT", "TSLA", "AMZN"]; - var baseTimestamp = DateTime.UtcNow.AddMinutes(-30); + DateTime baseTimestamp = DateTime.UtcNow.AddMinutes(-30); for (int i = 0; i < NumberOfRecords; i++) { - var trade = CreateTradeInstance(i, baseTimestamp, tickers[random.Next(tickers.Length)], random); + T trade = CreateTradeInstance(i, baseTimestamp, tickers[random.Next(tickers.Length)], random); Trades.Add(trade); } diff --git a/src/Eftdb.Design/TimescaleDatabaseModelFactory.cs b/src/Eftdb.Design/TimescaleDatabaseModelFactory.cs index 86f833e..8fef8bd 100644 --- a/src/Eftdb.Design/TimescaleDatabaseModelFactory.cs +++ b/src/Eftdb.Design/TimescaleDatabaseModelFactory.cs @@ -28,9 +28,7 @@ public override DatabaseModel Create(DbConnection connection, DatabaseModelFacto DatabaseModel databaseModel = base.Create(connection, options); // Extract all TimescaleDB features from the database - var allFeatureData = _features - .Select(feature => feature.Extractor.Extract(connection)) - .ToList(); + List> allFeatureData = [.. _features.Select(feature => feature.Extractor.Extract(connection))]; // Apply annotations to tables/views in the model foreach (DatabaseTable table in databaseModel.Tables) @@ -42,7 +40,7 @@ public override DatabaseModel Create(DbConnection connection, DatabaseModelFacto // Apply each feature's annotations if the table has that feature for (int i = 0; i < _features.Count; i++) { - var featureData = allFeatureData[i]; + Dictionary<(string Schema, string TableName), object> featureData = allFeatureData[i]; if (featureData.TryGetValue(tableKey, out object? featureInfo)) { _features[i].Applier.ApplyAnnotations(table, featureInfo); diff --git a/src/Eftdb/Abstractions/TimescaleCopyConfig.cs b/src/Eftdb/Abstractions/TimescaleCopyConfig.cs index a815cc2..6b91a46 100644 --- a/src/Eftdb/Abstractions/TimescaleCopyConfig.cs +++ b/src/Eftdb/Abstractions/TimescaleCopyConfig.cs @@ -58,10 +58,10 @@ public TimescaleCopyConfig() if (MapClrTypeToNpgsqlDbType(property.PropertyType, out NpgsqlDbType dbType)) { // Auto-discover properties and create compiled getters for them. - var parameter = Expression.Parameter(typeof(T), "x"); - var member = Expression.Property(parameter, property); - var conversion = Expression.Convert(member, typeof(object)); - var lambda = Expression.Lambda>(conversion, parameter); + ParameterExpression parameter = Expression.Parameter(typeof(T), "x"); + MemberExpression member = Expression.Property(parameter, property); + UnaryExpression conversion = Expression.Convert(member, typeof(object)); + Expression> lambda = Expression.Lambda>(conversion, parameter); ColumnMappings[property.Name] = (lambda.Compile(), dbType); } @@ -124,7 +124,7 @@ public TimescaleCopyConfig MapColumn(string columnName, Expression Generate(CreateContinuousAggregateOperation operation) } // Build the complete CREATE MATERIALIZED VIEW statement as a single string - var sqlBuilder = new System.Text.StringBuilder(); + StringBuilder sqlBuilder = new(); sqlBuilder.Append($"CREATE MATERIALIZED VIEW {qualifiedIdentifier}"); sqlBuilder.AppendLine(); sqlBuilder.Append($"WITH ({string.Join(", ", withOptions)}) AS"); diff --git a/src/Eftdb/Generators/HypertableOperationGenerator.cs b/src/Eftdb/Generators/HypertableOperationGenerator.cs index d3004f0..4a39817 100644 --- a/src/Eftdb/Generators/HypertableOperationGenerator.cs +++ b/src/Eftdb/Generators/HypertableOperationGenerator.cs @@ -1,5 +1,6 @@ using CmdScale.EntityFrameworkCore.TimescaleDB.Abstractions; using CmdScale.EntityFrameworkCore.TimescaleDB.Operations; +using System.Text; namespace CmdScale.EntityFrameworkCore.TimescaleDB.Generators { @@ -16,7 +17,6 @@ public HypertableOperationGenerator(bool isDesignTime = false) } sqlHelper = new SqlBuilderHelper(quoteString); - } public List Generate(CreateHypertableOperation operation) @@ -24,48 +24,51 @@ public List Generate(CreateHypertableOperation operation) string qualifiedTableName = sqlHelper.Regclass(operation.TableName, operation.Schema); string qualifiedIdentifier = sqlHelper.QualifiedIdentifier(operation.TableName, operation.Schema); - string migrateDataParam = operation.MigrateData ? ", migrate_data => true" : ""; + List statements = []; + List communityStatements = []; - List statements = - [ - $"SELECT create_hypertable({qualifiedTableName}, '{operation.TimeColumnName}'{migrateDataParam});" - ]; + // Build create_hypertable with chunk_time_interval if provided + StringBuilder createHypertableCall = new(); + createHypertableCall.Append($"SELECT create_hypertable({qualifiedTableName}, '{operation.TimeColumnName}'"); + createHypertableCall.Append(operation.MigrateData ? ", migrate_data => true" : ""); - // ChunkTimeInterval if (!string.IsNullOrEmpty(operation.ChunkTimeInterval)) { // Check if the interval is a plain number (e.g., for microseconds). if (long.TryParse(operation.ChunkTimeInterval, out _)) { // If it's a number, don't wrap it in quotes. - statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, {operation.ChunkTimeInterval}::bigint);"); + createHypertableCall.Append($", chunk_time_interval => {operation.ChunkTimeInterval}::bigint"); } else { // If it's a string like '7 days', wrap it in quotes. - statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, INTERVAL '{operation.ChunkTimeInterval}');"); + createHypertableCall.Append($", chunk_time_interval => INTERVAL '{operation.ChunkTimeInterval}'"); } } - // EnableCompression + createHypertableCall.Append(");"); + statements.Add(createHypertableCall.ToString()); + + // EnableCompression (Community Edition only) if (operation.EnableCompression || operation.ChunkSkipColumns?.Count > 0) { bool enableCompression = operation.EnableCompression || operation.ChunkSkipColumns != null && operation.ChunkSkipColumns.Count > 0; - statements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {enableCompression.ToString().ToLower()});"); + communityStatements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {enableCompression.ToString().ToLower()});"); } - // ChunkSkipColumns + // ChunkSkipColumns (Community Edition only) if (operation.ChunkSkipColumns != null && operation.ChunkSkipColumns.Count > 0) { - statements.Add("SET timescaledb.enable_chunk_skipping = 'ON';"); + communityStatements.Add("SET timescaledb.enable_chunk_skipping = 'ON';"); foreach (string column in operation.ChunkSkipColumns) { - statements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');"); + communityStatements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');"); } } - // AdditionalDimensions + // AdditionalDimensions (Available in both editions) if (operation.AdditionalDimensions != null && operation.AdditionalDimensions.Count > 0) { foreach (Dimension dimension in operation.AdditionalDimensions) @@ -87,6 +90,11 @@ public List Generate(CreateHypertableOperation operation) } } + // Add wrapped community statements if any exist + if (communityStatements.Count > 0) + { + statements.Add(WrapCommunityFeatures(communityStatements)); + } return statements; } @@ -96,45 +104,52 @@ public List Generate(AlterHypertableOperation operation) string qualifiedIdentifier = sqlHelper.QualifiedIdentifier(operation.TableName, operation.Schema); List statements = []; + List communityStatements = []; - // Check for ChunkTimeInterval change + // Check for ChunkTimeInterval change (Available in both editions) if (operation.ChunkTimeInterval != operation.OldChunkTimeInterval) { + StringBuilder setChunkTimeInterval = new(); + setChunkTimeInterval.Append($"SELECT set_chunk_time_interval({qualifiedTableName}, "); + // Check if the interval is a plain number (e.g., for microseconds). if (long.TryParse(operation.ChunkTimeInterval, out _)) { // If it's a number, don't wrap it in quotes. - statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, {operation.ChunkTimeInterval}::bigint);"); + setChunkTimeInterval.Append($"{operation.ChunkTimeInterval}::bigint"); } else { // If it's a string like '7 days', wrap it in quotes. - statements.Add($"SELECT set_chunk_time_interval({qualifiedTableName}, INTERVAL '{operation.ChunkTimeInterval}');"); + setChunkTimeInterval.Append($"INTERVAL '{operation.ChunkTimeInterval}'"); } + + setChunkTimeInterval.Append(");"); + statements.Add(setChunkTimeInterval.ToString()); } - // Check for EnableCompression change + // Check for EnableCompression change (Community Edition only) bool newCompressionState = operation.EnableCompression || operation.ChunkSkipColumns != null && operation.ChunkSkipColumns.Any(); bool oldCompressionState = operation.OldEnableCompression || operation.OldChunkSkipColumns != null && operation.OldChunkSkipColumns.Any(); if (newCompressionState != oldCompressionState) { string compressionValue = newCompressionState.ToString().ToLower(); - statements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {compressionValue});"); + communityStatements.Add($"ALTER TABLE {qualifiedIdentifier} SET (timescaledb.compress = {compressionValue});"); } - // Handle ChunkSkipColumns + // Handle ChunkSkipColumns (Community Edition only) IReadOnlyList newColumns = operation.ChunkSkipColumns ?? []; IReadOnlyList oldColumns = operation.OldChunkSkipColumns ?? []; List addedColumns = [.. newColumns.Except(oldColumns)]; if (addedColumns.Count != 0) { - statements.Add("SET timescaledb.enable_chunk_skipping = 'ON';"); + communityStatements.Add("SET timescaledb.enable_chunk_skipping = 'ON';"); foreach (string column in addedColumns) { - statements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');"); + communityStatements.Add($"SELECT enable_chunk_skipping({qualifiedTableName}, '{column}');"); } } @@ -143,7 +158,7 @@ public List Generate(AlterHypertableOperation operation) { foreach (string column in removedColumns) { - statements.Add($"SELECT disable_chunk_skipping({qualifiedTableName}, '{column}');"); + communityStatements.Add($"SELECT disable_chunk_skipping({qualifiedTableName}, '{column}');"); } } @@ -194,8 +209,41 @@ public List Generate(AlterHypertableOperation operation) statements.Add($"-- WARNING: TimescaleDB does not support removing dimensions. The following dimensions cannot be removed: {dimensionList}"); } + // Add wrapped community statements if any exist + if (communityStatements.Count > 0) + { + statements.Add(WrapCommunityFeatures(communityStatements)); + } return statements; } - } -} + /// + /// Wraps multiple SQL statements in a single license check block to ensure they only run on Community Edition. + /// + private static string WrapCommunityFeatures(List sqlStatements) + { + StringBuilder sb = new(); + sb.AppendLine("DO $$"); + sb.AppendLine("DECLARE"); + sb.AppendLine(" license TEXT;"); + sb.AppendLine("BEGIN"); + sb.AppendLine(" license := current_setting('timescaledb.license', true);"); + sb.AppendLine(" "); + sb.AppendLine(" IF license IS NULL OR license != 'apache' THEN"); + + foreach (string sql in sqlStatements) + { + // Remove trailing semicolon and escape single quotes for EXECUTE + string cleanSql = sql.TrimEnd(';').Replace("'", "''"); + sb.AppendLine($" EXECUTE '{cleanSql}';"); + } + + sb.AppendLine(" ELSE"); + sb.AppendLine(" RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition';"); + sb.AppendLine(" END IF;"); + sb.AppendLine("END $$;"); + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/Eftdb/TimescaleDbCopyExtensions.cs b/src/Eftdb/TimescaleDbCopyExtensions.cs index 3324c2f..84734b8 100644 --- a/src/Eftdb/TimescaleDbCopyExtensions.cs +++ b/src/Eftdb/TimescaleDbCopyExtensions.cs @@ -55,7 +55,7 @@ public static async Task BulkCopyAsync( await writer.StartRowAsync(); // Write each configured column in the specified order - foreach (var (Getter, DbType) in config.ColumnMappings.Values) + foreach ((Func? Getter, NpgsqlTypes.NpgsqlDbType DbType) in config.ColumnMappings.Values) { object? value = Getter(item); await writer.WriteAsync(value, DbType); diff --git a/tests/Eftdb.Tests/Generators/HypertableOperationGeneratorComprehensiveTests.cs b/tests/Eftdb.Tests/Generators/HypertableOperationGeneratorComprehensiveTests.cs index 61c9677..b2df5d4 100644 --- a/tests/Eftdb.Tests/Generators/HypertableOperationGeneratorComprehensiveTests.cs +++ b/tests/Eftdb.Tests/Generators/HypertableOperationGeneratorComprehensiveTests.cs @@ -61,8 +61,7 @@ public void DesignTime_Create_WithRangeDimension_GeneratesCorrectCode() }; string expected = @".Sql(@"" - SELECT create_hypertable('public.""""events""""', 'event_time'); - SELECT set_chunk_time_interval('public.""""events""""', INTERVAL '1 day'); + SELECT create_hypertable('public.""""events""""', 'event_time', chunk_time_interval => INTERVAL '1 day'); SELECT add_dimension('public.""""events""""', by_range('received_time', INTERVAL '7 days')); "")"; @@ -115,8 +114,7 @@ public void DesignTime_Create_WithChunkTimeIntervalAsMicroseconds_GeneratesCorre }; string expected = @".Sql(@"" - SELECT create_hypertable('public.""""high_freq_data""""', 'ts'); - SELECT set_chunk_time_interval('public.""""high_freq_data""""', 86400000000::bigint); + SELECT create_hypertable('public.""""high_freq_data""""', 'ts', chunk_time_interval => 86400000000::bigint); "")"; // Act @@ -140,7 +138,18 @@ public void DesignTime_Create_CompressionWithoutChunkSkipping_GeneratesCorrectCo string expected = @".Sql(@"" SELECT create_hypertable('public.""""compressed_data""""', 'time'); - ALTER TABLE """"public"""".""""compressed_data"""" SET (timescaledb.compress = true); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'ALTER TABLE """"public"""".""""compressed_data"""" SET (timescaledb.compress = true)'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -415,7 +424,18 @@ public void DesignTime_Alter_DisablingCompression_GeneratesCorrectCode() }; string expected = @".Sql(@"" - ALTER TABLE """"public"""".""""decompress"""" SET (timescaledb.compress = false); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'ALTER TABLE """"public"""".""""decompress"""" SET (timescaledb.compress = false)'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -438,9 +458,20 @@ public void DesignTime_Alter_AddingChunkSkipColumn_GeneratesCorrectSequence() }; string expected = @".Sql(@"" - SET timescaledb.enable_chunk_skipping = 'ON'; - SELECT enable_chunk_skipping('public.""""add_skip""""', 'col2'); - SELECT enable_chunk_skipping('public.""""add_skip""""', 'col3'); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'SET timescaledb.enable_chunk_skipping = ''ON'''; + EXECUTE 'SELECT enable_chunk_skipping(''public.""""add_skip""""'', ''col2'')'; + EXECUTE 'SELECT enable_chunk_skipping(''public.""""add_skip""""'', ''col3'')'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -463,7 +494,18 @@ public void DesignTime_Alter_RemovingChunkSkipColumn_GeneratesDisableCommands() }; string expected = @".Sql(@"" - SELECT disable_chunk_skipping('public.""""remove_skip""""', 'remove_this'); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'SELECT disable_chunk_skipping(''public.""""remove_skip""""'', ''remove_this'')'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -550,8 +592,8 @@ public void Runtime_Alter_ChunkSkipping_RequiresSETCommand() string result = GetRuntimeSql(operation); // Assert - Must SET enable_chunk_skipping = 'ON' before enable_chunk_skipping() - Assert.Contains("SET timescaledb.enable_chunk_skipping = 'ON'", result); - Assert.Contains("enable_chunk_skipping('public.\"skip_test\"', 'new_col')", result); + Assert.Contains("SET timescaledb.enable_chunk_skipping = ''ON''", result); + Assert.Contains("enable_chunk_skipping(''public.\"skip_test\"'', ''new_col'')", result); } #endregion diff --git a/tests/Eftdb.Tests/Generators/HypertableOperationGeneratorTests.cs b/tests/Eftdb.Tests/Generators/HypertableOperationGeneratorTests.cs index 618f72b..8c98604 100644 --- a/tests/Eftdb.Tests/Generators/HypertableOperationGeneratorTests.cs +++ b/tests/Eftdb.Tests/Generators/HypertableOperationGeneratorTests.cs @@ -64,12 +64,22 @@ public void Generate_Create_with_all_options_generates_comprehensive_sql() }; string expected = @".Sql(@"" - SELECT create_hypertable('custom_schema.""""FullTable""""', 'EventTime'); - SELECT set_chunk_time_interval('custom_schema.""""FullTable""""', INTERVAL '1 day'); - ALTER TABLE """"custom_schema"""".""""FullTable"""" SET (timescaledb.compress = true); - SET timescaledb.enable_chunk_skipping = 'ON'; - SELECT enable_chunk_skipping('custom_schema.""""FullTable""""', 'DeviceId'); + SELECT create_hypertable('custom_schema.""""FullTable""""', 'EventTime', chunk_time_interval => INTERVAL '1 day'); SELECT add_dimension('custom_schema.""""FullTable""""', by_hash('LocationId', 4)); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'ALTER TABLE """"custom_schema"""".""""FullTable"""" SET (timescaledb.compress = true)'; + EXECUTE 'SET timescaledb.enable_chunk_skipping = ''ON'''; + EXECUTE 'SELECT enable_chunk_skipping(''custom_schema.""""FullTable""""'', ''DeviceId'')'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -94,9 +104,20 @@ public void Generate_Alter_WhenAddingChunkSkippingToUncompressedTable_ShouldAlso }; string expected = @".Sql(@"" - ALTER TABLE """"custom_schema"""".""""Metrics"""" SET (timescaledb.compress = true); - SET timescaledb.enable_chunk_skipping = 'ON'; - SELECT enable_chunk_skipping('custom_schema.""""Metrics""""', 'device_id'); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'ALTER TABLE """"custom_schema"""".""""Metrics"""" SET (timescaledb.compress = true)'; + EXECUTE 'SET timescaledb.enable_chunk_skipping = ''ON'''; + EXECUTE 'SELECT enable_chunk_skipping(''custom_schema.""""Metrics""""'', ''device_id'')'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -121,7 +142,18 @@ public void Generate_Alter_when_changing_compression_generates_correct_sql() }; string expected = @".Sql(@"" - ALTER TABLE """"public"""".""""SensorData"""" SET (timescaledb.compress = true); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'ALTER TABLE """"public"""".""""SensorData"""" SET (timescaledb.compress = true)'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -144,9 +176,20 @@ public void Generate_Alter_when_adding_and_removing_skip_columns_generates_corre }; string expected = @".Sql(@"" - SET timescaledb.enable_chunk_skipping = 'ON'; - SELECT enable_chunk_skipping('metrics_schema.""""Metrics""""', 'service'); - SELECT disable_chunk_skipping('metrics_schema.""""Metrics""""', 'region'); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'SET timescaledb.enable_chunk_skipping = ''ON'''; + EXECUTE 'SELECT enable_chunk_skipping(''metrics_schema.""""Metrics""""'', ''service'')'; + EXECUTE 'SELECT disable_chunk_skipping(''metrics_schema.""""Metrics""""'', ''region'')'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -193,8 +236,19 @@ public void Generate_Alter_WhenRemovingLastChunkSkipColumn_ShouldDisableCompress ChunkSkipColumns = [] }; string expected = @".Sql(@"" - ALTER TABLE """"public"""".""""Logs"""" SET (timescaledb.compress = false); - SELECT disable_chunk_skipping('public.""""Logs""""', 'trace_id'); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'ALTER TABLE """"public"""".""""Logs"""" SET (timescaledb.compress = false)'; + EXECUTE 'SELECT disable_chunk_skipping(''public.""""Logs""""'', ''trace_id'')'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act @@ -272,12 +326,22 @@ public void Generate_Create_When_MigrateData_True_With_All_Options_Generates_Com }; string expected = @".Sql(@"" - SELECT create_hypertable('custom_schema.""""CompleteTable""""', 'EventTime', migrate_data => true); - SELECT set_chunk_time_interval('custom_schema.""""CompleteTable""""', INTERVAL '1 day'); - ALTER TABLE """"custom_schema"""".""""CompleteTable"""" SET (timescaledb.compress = true); - SET timescaledb.enable_chunk_skipping = 'ON'; - SELECT enable_chunk_skipping('custom_schema.""""CompleteTable""""', 'DeviceId'); + SELECT create_hypertable('custom_schema.""""CompleteTable""""', 'EventTime', migrate_data => true, chunk_time_interval => INTERVAL '1 day'); SELECT add_dimension('custom_schema.""""CompleteTable""""', by_hash('LocationId', 4)); + DO $$ + DECLARE + license TEXT; + BEGIN + license := current_setting('timescaledb.license', true); + + IF license IS NULL OR license != 'apache' THEN + EXECUTE 'ALTER TABLE """"custom_schema"""".""""CompleteTable"""" SET (timescaledb.compress = true)'; + EXECUTE 'SET timescaledb.enable_chunk_skipping = ''ON'''; + EXECUTE 'SELECT enable_chunk_skipping(''custom_schema.""""CompleteTable""""'', ''DeviceId'')'; + ELSE + RAISE WARNING 'Skipping Community Edition features (compression, chunk skipping) - not available in Apache Edition'; + END IF; + END $$; "")"; // Act