Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public override DatabaseModel Create(DbConnection connection, DatabaseModelFacto

private static Dictionary<(string, string), HypertableInfo> GetHypertables(DbConnection connection)
{
bool wasOpen = connection.State == System.Data.ConnectionState.Open;
bool wasOpen = connection.State == ConnectionState.Open;
if (!wasOpen)
{
connection.Open();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(TimescaleContext).Assembly);
modelBuilder.HasDefaultSchema("custom_schema");
}
}
}
20 changes: 20 additions & 0 deletions CmdScale.EntityFrameworkCore.TimescaleDB.Example/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@

IHost host = builder.Build();

#if false
// --- Code to run Database.EnsureCreatedAsync() ---
// NOTE: Set the #if to false to disable this block or to true to enable it.
using (IServiceScope scope = host.Services.CreateScope())
{
IServiceProvider services = scope.ServiceProvider;
try
{
TimescaleContext context = services.GetRequiredService<TimescaleContext>();
Console.WriteLine("Applying Database.EnsureCreatedAsync()...");
await context.Database.EnsureCreatedAsync();
Console.WriteLine("Database setup complete.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while creating the database: {ex.Message}");
}
}
#endif

Console.WriteLine("TimescaleDB EF Core Demo");
Console.WriteLine("------------------------------------");
Console.WriteLine("Run 'dotnet ef migrations add <MigrationName>' to generate a migration.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ public void Generate_Create_with_minimal_details_generates_correct_sql()
CreateHypertableOperation operation = new()
{
TableName = "MinimalTable",
Schema = "public",
TimeColumnName = "Timestamp"
};

string expected = @".Sql(@""
SELECT create_hypertable('""""MinimalTable""""', 'Timestamp');
SELECT create_hypertable('public.""""MinimalTable""""', 'Timestamp');
"")";

// Act
Expand All @@ -51,6 +52,7 @@ public void Generate_Create_with_all_options_generates_comprehensive_sql()
CreateHypertableOperation operation = new()
{
TableName = "FullTable",
Schema = "custom_schema",
TimeColumnName = "EventTime",
ChunkTimeInterval = "1 day",
EnableCompression = true,
Expand All @@ -62,12 +64,12 @@ public void Generate_Create_with_all_options_generates_comprehensive_sql()
};

string expected = @".Sql(@""
SELECT create_hypertable('""""FullTable""""', 'EventTime');
SELECT set_chunk_time_interval('""""FullTable""""', INTERVAL '1 day');
ALTER TABLE """"FullTable"""" SET (timescaledb.compress = true);
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('""""FullTable""""', 'DeviceId');
SELECT add_dimension('""""FullTable""""', by_hash('LocationId', 4));
SELECT enable_chunk_skipping('custom_schema.""""FullTable""""', 'DeviceId');
SELECT add_dimension('custom_schema.""""FullTable""""', by_hash('LocationId', 4));
"")";

// Act
Expand All @@ -84,16 +86,17 @@ public void Generate_Alter_WhenAddingChunkSkippingToUncompressedTable_ShouldAlso
AlterHypertableOperation operation = new()
{
TableName = "Metrics",
Schema = "custom_schema",
OldEnableCompression = false,
OldChunkSkipColumns = [],
EnableCompression = false,
ChunkSkipColumns = ["device_id"]
};

string expected = @".Sql(@""
ALTER TABLE """"Metrics"""" SET (timescaledb.compress = true);
ALTER TABLE """"custom_schema"""".""""Metrics"""" SET (timescaledb.compress = true);
SET timescaledb.enable_chunk_skipping = 'ON';
SELECT enable_chunk_skipping('""""Metrics""""', 'device_id');
SELECT enable_chunk_skipping('custom_schema.""""Metrics""""', 'device_id');
"")";

// Act
Expand All @@ -112,12 +115,13 @@ public void Generate_Alter_when_changing_compression_generates_correct_sql()
AlterHypertableOperation operation = new()
{
TableName = "SensorData",
Schema = "public",
EnableCompression = true,
OldEnableCompression = false
};

string expected = @".Sql(@""
ALTER TABLE """"SensorData"""" SET (timescaledb.compress = true);
ALTER TABLE """"public"""".""""SensorData"""" SET (timescaledb.compress = true);
"")";

// Act
Expand All @@ -134,14 +138,15 @@ public void Generate_Alter_when_adding_and_removing_skip_columns_generates_corre
AlterHypertableOperation operation = new()
{
TableName = "Metrics",
Schema = "metrics_schema",
ChunkSkipColumns = ["host", "service"],
OldChunkSkipColumns = ["host", "region"]
};

string expected = @".Sql(@""
SET timescaledb.enable_chunk_skipping = 'ON';
SELECT enable_chunk_skipping('""""Metrics""""', 'service');
SELECT disable_chunk_skipping('""""Metrics""""', 'region');
SELECT enable_chunk_skipping('metrics_schema.""""Metrics""""', 'service');
SELECT disable_chunk_skipping('metrics_schema.""""Metrics""""', 'region');
"")";

// Act
Expand All @@ -158,6 +163,7 @@ public void Generate_Alter_when_no_properties_change_generates_no_sql()
AlterHypertableOperation operation = new()
{
TableName = "NoChangeTable",
Schema = "public",
EnableCompression = true,
OldEnableCompression = true,
ChunkTimeInterval = "7 days",
Expand All @@ -180,14 +186,15 @@ public void Generate_Alter_WhenRemovingLastChunkSkipColumn_ShouldDisableCompress
AlterHypertableOperation operation = new()
{
TableName = "Logs",
Schema = "public",
OldEnableCompression = false,
OldChunkSkipColumns = ["trace_id"],
EnableCompression = false,
ChunkSkipColumns = []
};
string expected = @".Sql(@""
ALTER TABLE """"Logs"""" SET (timescaledb.compress = false);
SELECT disable_chunk_skipping('""""Logs""""', 'trace_id');
ALTER TABLE """"public"""".""""Logs"""" SET (timescaledb.compress = false);
SELECT disable_chunk_skipping('public.""""Logs""""', 'trace_id');
"")";

// Act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ public void Generate_Add_with_minimal_details_creates_only_add_policy_sql()
// Arrange
AddReorderPolicyOperation operation = new()
{
Schema = "public",
TableName = "TestTable",
IndexName = "IX_TestTable_Time"
};

string expected = @".Sql(@""
SELECT add_reorder_policy('""""TestTable""""', 'IX_TestTable_Time');
SELECT add_reorder_policy('public.""""TestTable""""', 'IX_TestTable_Time');
"")";

// Act
Expand All @@ -47,20 +48,21 @@ public void Generate_Add_with_non_default_schedule_creates_add_and_alter_sql()
DateTime testDate = new(2025, 10, 20, 12, 30, 0, DateTimeKind.Utc);
AddReorderPolicyOperation operation = new()
{
Schema = "custom",
TableName = "TestTable",
IndexName = "IX_TestTable_Time",
InitialStart = testDate,
ScheduleInterval = "2 days",
MaxRuntime = "1 hour",
MaxRuntime = "1 hour",
MaxRetries = 5,
RetryPeriod = "10 minutes"
};

string expected = @".Sql(@""
SELECT add_reorder_policy('""""TestTable""""', 'IX_TestTable_Time', initial_start => '2025-10-20T12:30:00.0000000Z');
SELECT add_reorder_policy('custom.""""TestTable""""', 'IX_TestTable_Time', initial_start => '2025-10-20T12:30:00.0000000Z');
SELECT alter_job(job_id, schedule_interval => INTERVAL '2 days', max_runtime => INTERVAL '1 hour', max_retries => 5, retry_period => INTERVAL '10 minutes')
FROM timescaledb_information.jobs
WHERE proc_name = 'policy_reorder' AND hypertable_name = 'TestTable';
WHERE proc_name = 'policy_reorder' AND hypertable_schema = 'custom' AND hypertable_name = 'TestTable';
"")";

// Act
Expand All @@ -76,10 +78,14 @@ FROM timescaledb_information.jobs
public void Generate_Drop_creates_correct_remove_policy_sql()
{
// Arrange
DropReorderPolicyOperation operation = new() { TableName = "TestTable" };
DropReorderPolicyOperation operation = new()
{
Schema = "public",
TableName = "TestTable"
};

string expected = @".Sql(@""
SELECT remove_reorder_policy('""""TestTable""""', if_exists => true);
SELECT remove_reorder_policy('public.""""TestTable""""', if_exists => true);
"")";

// Act
Expand All @@ -97,6 +103,7 @@ public void Generate_Alter_when_only_job_settings_change_creates_only_alter_job_
// Arrange
AlterReorderPolicyOperation operation = new()
{
Schema = "metrics",
TableName = "TestTable",
// Fundamental properties are the same
IndexName = "IX_TestTable_Time",
Expand All @@ -111,7 +118,7 @@ public void Generate_Alter_when_only_job_settings_change_creates_only_alter_job_
string expected = @".Sql(@""
SELECT alter_job(job_id, schedule_interval => INTERVAL '2 days')
FROM timescaledb_information.jobs
WHERE proc_name = 'policy_reorder' AND hypertable_name = 'TestTable';
WHERE proc_name = 'policy_reorder' AND hypertable_schema = 'metrics' AND hypertable_name = 'TestTable';
"")";

// Act
Expand All @@ -127,6 +134,7 @@ public void Generate_Alter_when_fundamental_property_changes_creates_drop_and_ad
// Arrange
AlterReorderPolicyOperation operation = new()
{
Schema = "logs",
TableName = "TestTable",
IndexName = "IX_New_Name",
OldIndexName = "IX_Old_Name",
Expand All @@ -135,11 +143,11 @@ public void Generate_Alter_when_fundamental_property_changes_creates_drop_and_ad
};

string expected = @".Sql(@""
SELECT remove_reorder_policy('""""TestTable""""', if_exists => true);
SELECT add_reorder_policy('""""TestTable""""', 'IX_New_Name');
SELECT remove_reorder_policy('logs.""""TestTable""""', if_exists => true);
SELECT add_reorder_policy('logs.""""TestTable""""', 'IX_New_Name');
SELECT alter_job(job_id, schedule_interval => INTERVAL '2 days')
FROM timescaledb_information.jobs
WHERE proc_name = 'policy_reorder' AND hypertable_name = 'TestTable';
WHERE proc_name = 'policy_reorder' AND hypertable_schema = 'logs' AND hypertable_name = 'TestTable';
"")";

// Act
Expand All @@ -155,6 +163,7 @@ public void Generate_Alter_when_both_fundamental_and_job_settings_change_creates
// Arrange
AlterReorderPolicyOperation operation = new()
{
Schema = "public",
TableName = "TestTable",
IndexName = "IX_New_Name",
OldIndexName = "IX_Old_Name",
Expand All @@ -167,11 +176,11 @@ public void Generate_Alter_when_both_fundamental_and_job_settings_change_creates
};

string expected = @".Sql(@""
SELECT remove_reorder_policy('""""TestTable""""', if_exists => true);
SELECT add_reorder_policy('""""TestTable""""', 'IX_New_Name');
SELECT remove_reorder_policy('public.""""TestTable""""', if_exists => true);
SELECT add_reorder_policy('public.""""TestTable""""', 'IX_New_Name');
SELECT alter_job(job_id, schedule_interval => INTERVAL '2 days', max_retries => 5, retry_period => INTERVAL '10 minutes')
FROM timescaledb_information.jobs
WHERE proc_name = 'policy_reorder' AND hypertable_name = 'TestTable';
WHERE proc_name = 'policy_reorder' AND hypertable_schema = 'public' AND hypertable_name = 'TestTable';
"")";

// Act
Expand All @@ -181,4 +190,4 @@ FROM timescaledb_information.jobs
Assert.Equal(SqlHelper.NormalizeSql(expected), SqlHelper.NormalizeSql(result));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void Regclass_Runtime_ReturnsCorrectlyQuotedString()
// Arrange
SqlBuilderHelper helper = new(quoteString: "\"");
string tableName = "MyTable";
string expected = "'\"MyTable\"'";
string expected = "'public.\"MyTable\"'";

// Act
string result = helper.Regclass(tableName);
Expand All @@ -34,7 +34,7 @@ public void QualifiedIdentifier_Runtime_ReturnsCorrectlyQuotedString()
// Arrange
SqlBuilderHelper helper = new(quoteString: "\"");
string tableName = "MyTable";
string expected = "\"MyTable\"";
string expected = "\"public\".\"MyTable\"";

// Act
string result = helper.QualifiedIdentifier(tableName);
Expand All @@ -49,7 +49,7 @@ public void Regclass_DesignTime_ReturnsCorrectlyEscapedQuotedString()
// Arrange
SqlBuilderHelper helper = new(quoteString: "\"\"");
string tableName = "MyTable";
string expected = "'\"\"MyTable\"\"'";
string expected = "'public.\"\"MyTable\"\"'";

// Act
string result = helper.Regclass(tableName);
Expand All @@ -64,7 +64,7 @@ public void QualifiedIdentifier_DesignTime_ReturnsCorrectlyEscapedQuotedString()
// Arrange
SqlBuilderHelper helper = new(quoteString: "\"\"");
string tableName = "MyTable";
string expected = "\"\"MyTable\"\"";
string expected = "\"\"public\"\".\"\"MyTable\"\"";

// Act
string result = helper.QualifiedIdentifier(tableName);
Expand Down
1 change: 1 addition & 0 deletions CmdScale.EntityFrameworkCore.TimescaleDB/DefaultValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/// </summary>
public static class DefaultValues
{
public const string DefaultSchema = "public";
public const string ChunkTimeInterval = "7 days";
public const long ChunkTimeIntervalLong = 604_800_000_000L;
public const string ReorderPolicyScheduleInterval = "1 day";
Expand Down
Loading
Loading