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
1 change: 1 addition & 0 deletions src/TriggerBinding/SqlTriggerConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal static class SqlTriggerConstants
public const string LeasesTableAttemptCountColumnName = "_az_func_AttemptCount";
public const string LeasesTableLeaseExpirationTimeColumnName = "_az_func_LeaseExpirationTime";
public const string SysChangeVersionColumnName = "SYS_CHANGE_VERSION";
public const string LastAccessTimeColumnName = "LastAccessTime";
/// <summary>
/// The column names that are used in internal state tables and so can't exist in the target table
/// since that shares column names with the primary keys from each user table being monitored.
Expand Down
44 changes: 44 additions & 0 deletions test/Integration/SqlTriggerBindingIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common;
using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.TriggerBindingSamples;
using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common;
using static Microsoft.Azure.WebJobs.Extensions.Sql.SqlTriggerConstants;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -612,5 +613,48 @@ void TestExceptionMessageSeen(object sender, DataReceivedEventArgs e)
await changesTask;

}

/// <summary>
/// Tests that the GlobalState table has LastAccessTime column.
/// </summary>
/// <remarks>We call StartAsync which initializes the GlobalState and then drop the LastAccessTime column from the table and recall the StartAsync to check if the GlobalState has the column.</remarks>
[Fact]
public async void LastAccessTimeColumn_Created_OnStartup()
{

this.SetChangeTrackingForTable("Products");
string userFunctionId = "func-id";
IConfiguration configuration = new ConfigurationBuilder().Build();
var listener = new SqlTriggerListener<Product>(this.DbConnectionString, "dbo.Products", userFunctionId, Mock.Of<ITriggeredFunctionExecutor>(), Mock.Of<ILogger>(), configuration);
await listener.StartAsync(CancellationToken.None);
// Cancel immediately so the listener doesn't start processing the changes
await listener.StopAsync(CancellationToken.None);
//Check if LastAccessTime column exists in the GlobalState table
Assert.True(1 == (int)this.ExecuteScalar($@"SELECT 1 FROM sys.columns WHERE Name = N'{LastAccessTimeColumnName}' AND Object_ID = Object_ID(N'{GlobalStateTableName}')"), $"{GlobalStateTableName} should have {LastAccessTimeColumnName} column on creation");
// Delete default constraint(s) on LastAccessTime column before dropping it
string deleteDefaultContraint = $@"DECLARE @sql NVARCHAR(MAX)
WHILE 1=1
BEGIN
SELECT TOP 1 @sql = N'ALTER TABLE {GlobalStateTableName} DROP CONSTRAINT ['+dc.NAME+N']'
FROM sys.default_constraints dc
JOIN sys.columns c
ON c.default_object_id = dc.object_id
WHERE dc.parent_object_id = OBJECT_ID('{GlobalStateTableName}')
AND c.name = N'{LastAccessTimeColumnName}'
IF @@ROWCOUNT = 0 BREAK
EXEC (@sql)
END";
this.ExecuteNonQuery(deleteDefaultContraint);
// Delete the LastAccessTime column from GlobalState table.
this.ExecuteNonQuery($"ALTER TABLE {GlobalStateTableName} DROP COLUMN {LastAccessTimeColumnName}");

await listener.StartAsync(CancellationToken.None);
// Cancel immediately so the listener doesn't start processing the changes
await listener.StopAsync(CancellationToken.None);

//Check if LastAccessTime column exists in the GlobalState table
Assert.True(1 == (int)this.ExecuteScalar("SELECT 1 FROM sys.columns WHERE Name = N'LastAccessTime' AND Object_ID = Object_ID(N'[az_func].[GlobalState]')"), $"{GlobalStateTableName} should have {LastAccessTimeColumnName} column after restarting the listener.");

}
}
}