Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0c9ad52
log primarykeys (#367)
lucyzhang929 Sep 23, 2022
85e0d9e
Port fix to always disable telemetry to main (#373)
Charles-Gagnon Sep 27, 2022
6a01349
Only package extension
Charles-Gagnon Sep 27, 2022
a20541c
Merge pull request #378 from Azure/chgagnon/dontPackagePerf
Charles-Gagnon Sep 27, 2022
930e7fd
Add site event properties
Charles-Gagnon Oct 3, 2022
5c6d0b2
Merge pull request #380 from Azure/chgagnon/addProperties
Charles-Gagnon Oct 3, 2022
90a953e
add sql binding java annotations (#379)
lucyzhang929 Oct 3, 2022
2b043b8
Add test for multiple function hosts (#344)
JatinSanghvi Oct 5, 2022
ed8b268
Add comment for explaining token cancellation (#387)
AmeyaRele Oct 10, 2022
339b874
Allow columns with default values to be excluded from the POCO (#388)
lucyzhang929 Oct 11, 2022
203837f
Fix folder used for Integration tests (#386)
lucyzhang929 Oct 11, 2022
72751fe
Update README and code comments (#391)
Charles-Gagnon Oct 12, 2022
04fc7c4
Copy latest sql dll to extension bundle in pipeline (#395)
lucyzhang929 Oct 13, 2022
66a1d06
Add additional logging when opening connections (#397)
Charles-Gagnon Oct 13, 2022
0b6e647
Merge branch 'main' into triggerbindings
Charles-Gagnon Oct 13, 2022
b966b42
Merge pull request #398 from Azure/chgagnon/mergeFromMain
Charles-Gagnon Oct 13, 2022
3a4ddd3
Fix polling size override using wrong configuration value
Charles-Gagnon Oct 14, 2022
2e0f345
Fixes
Charles-Gagnon Oct 14, 2022
360587c
Update comments
Charles-Gagnon Oct 14, 2022
42d301e
typo
Charles-Gagnon Oct 14, 2022
450180e
more changes
Charles-Gagnon Oct 14, 2022
a150c9f
Merge pull request #401 from Azure/chgagnon/fixPollingSize
Charles-Gagnon Oct 14, 2022
95fd712
Merge branch 'triggerbindings' into release/LogicApps
Charles-Gagnon Oct 18, 2022
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 Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
<DelaySign>True</DelaySign>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)/SQL2003.snk</AssemblyOriginatorKeyFile>
<IsPackable>false</IsPackable>
</PropertyGroup>
</Project>
47 changes: 43 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,14 @@ Azure SQL bindings for Azure Functions are supported for:
- [ICollector&lt;T&gt;/IAsyncCollector&lt;T&gt;](#icollectortiasynccollectort)
- [Array](#array)
- [Single Row](#single-row)
- [Primary Keys and Identity Columns](#primary-keys-and-identity-columns)
- [Primary Key Special Cases](#primary-key-special-cases)
- [Identity Columns](#identity-columns)
- [Columns with Default Values](#columns-with-default-values)
- [Trigger Binding](#trigger-binding)
- [Change Tracking](#change-tracking)
- [Internal State Tables](#internal-state-tables)
- [az_func.GlobalState](#az_funcglobalstate)
- [az_func.Leases_*](#az_funcleases_)
- [Trigger Samples](#trigger-samples)
- [Known Issues](#known-issues)
- [Telemetry](#telemetry)
Expand Down Expand Up @@ -826,20 +831,26 @@ public static IActionResult Run(
}
```

#### Primary Keys and Identity Columns
#### Primary Key Special Cases

Normally Output Bindings require two things :

1. The table being upserted to contains a Primary Key constraint (composed of one or more columns)
2. Each of those columns must be present in the POCO object used in the attribute

If either of these are false then an error will be thrown.
Normally if either of these are false then an error will be thrown. Below are the situations in which this is not the case :

This changes if one of the primary key columns is an identity column though. In that case there are two options based on how the function defines the output object:
##### Identity Columns
In the case where one of the primary key columns is an identity column, there are two options based on how the function defines the output object:

1. If the identity column isn't included in the output object then a straight insert is always performed with the other column values. See [AddProductWithIdentityColumn](./samples/samples-csharp/OutputBindingSamples/AddProductWithIdentityColumn.cs) for an example.
2. If the identity column is included (even if it's an optional nullable value) then a merge is performed similar to what happens when no identity column is present. This merge will either insert a new row or update an existing row based on the existence of a row that matches the primary keys (including the identity column). See [AddProductWithIdentityColumnIncluded](./samples/samples-csharp/OutputBindingSamples/AddProductWithIdentityColumnIncluded.cs) for an example.

##### Columns with Default Values
In the case where one of the primary key columns has a default value, there are also two options based on how the function defines the output object:
1. If the column with a default value is not included in the output object, then a straight insert is always performed with the other values. See [AddProductWithDefaultPK](./samples/samples-csharp/OutputBindingSamples/AddProductWithDefaultPK.cs) for an example.
2. If the column with a default value is included then a merge is performed similar to what happens when no default column is present. If there is a nullable column with a default value, then the provided column value in the output object will be upserted even if it is null.

### Trigger Binding

> **NOTE:** Trigger binding support is only available for C# functions at present.
Expand Down Expand Up @@ -869,6 +880,34 @@ The trigger binding utilizes SQL [change tracking](https://docs.microsoft.com/sq

> **NOTE:** The leases table contains all columns corresponding to the primary key from the user table and three additional columns named `_az_func_ChangeVersion`, `_az_func_AttemptCount` and `_az_func_LeaseExpirationTime`. If any of the primary key columns happen to have the same name, that will result in an error message listing any conflicts. In this case, the listed primary key columns must be renamed for the trigger to work.

#### Internal State Tables

The trigger functionality creates several tables to use for tracking the current state of the trigger. This allows state to be persisted across sessions and for multiple instances of a trigger binding to execute in parallel (for scaling purposes).

In addition, a schema named `az_func` will be created that the tables will belong to.

The login the trigger is configured to use must be given permissions to create these tables and schema. If not, then an error will be thrown and the trigger will fail to run.

If the tables are deleted or modified, then unexpected behavior may occur. To reset the state of the triggers, first stop all currently running functions with trigger bindings and then either truncate or delete the tables. The next time a function with a trigger binding is started, it will recreate the tables as necessary.

##### az_func.GlobalState

This table stores information about each function being executed, what table that function is watching and what the [last sync state](https://learn.microsoft.com/sql/relational-databases/track-changes/work-with-change-tracking-sql-server) that has been processed.

##### az_func.Leases_*

A `Leases_*` table is created for every unique instance of a function and table. The full name will be in the format `Leases_<FunctionId>_<TableId>` where `<FunctionId>` is generated from the function ID and `<TableId>` is the object ID of the table being tracked. Such as `Leases_7d12c06c6ddff24c_1845581613`.

This table is used to ensure that all changes are processed and that no change is processed more than once. This table consists of two groups of columns:

* A column for each column in the primary key of the target table - used to identify the row that it maps to in the target table
* A couple columns for tracking the state of each row. These are:
* `_az_func_ChangeVersion` for the change version of the row currently being processed
* `_az_func_AttemptCount` for tracking the number of times that a change has attempted to be processed to avoid getting stuck trying to process a change it's unable to handle
* `_az_func_LeaseExpirationTime` for tracking when the lease on this row for a particular instance is set to expire. This ensures that if an instance exits unexpectedly another instance will be able to pick up and process any changes it had leases for after the expiration time has passed.

A row is created for every row in the target table that is modified. These are then cleaned up after the changes are processed for a set of changes corresponding to a change tracking sync version.

#### Trigger Samples
The trigger binding takes two [arguments](https://github.com/Azure/azure-functions-sql-extension/blob/main/src/TriggerBinding/SqlTriggerAttribute.cs)

Expand Down
12 changes: 12 additions & 0 deletions builds/azure-pipelines/template-steps-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ steps:
displayName: 'Set npm installation path for Windows'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))

- bash: echo "##vso[task.setvariable variable=azureFunctionsExtensionBundlePath]$(func GetExtensionBundlePath)"
displayName: 'Set Azure Functions extension bundle path'
workingDirectory: $(Build.SourcesDirectory)/samples/samples-js

- task: DockerInstaller@0
displayName: Docker Installer
inputs:
Expand Down Expand Up @@ -67,6 +71,14 @@ steps:
projects: '${{ parameters.solution }}'
arguments: '--configuration ${{ parameters.configuration }} -p:GeneratePackageOnBuild=false -p:Version=${{ parameters.binariesVersion }}'

- task: CopyFiles@2
displayName: 'Copy Sql extension dll to Azure Functions extension bundle'
inputs:
sourceFolder: $(Build.SourcesDirectory)/src/bin/${{ parameters.configuration }}/netstandard2.0
contents: Microsoft.Azure.WebJobs.Extensions.Sql.dll
targetFolder: $(azureFunctionsExtensionBundlePath)/bin
overWrite: true

- script: |
npm install
npm run lint
Expand Down
120 changes: 120 additions & 0 deletions java-library/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.microsoft.azure.functions</groupId>
<artifactId>azure-functions-java-library-sql</artifactId>
<version>0.0.0</version>
<packaging>jar</packaging>

<parent>
<groupId>com.microsoft.maven</groupId>
<artifactId>java-8-parent</artifactId>
<version>8.0.1</version>
</parent>

<name>Microsoft Azure Functions Java SQL Types</name>
<description>This package contains all Java annotations to interact with Microsoft Azure Functions runtime for SQL Bindings.</description>
<url>https://aka.ms/sqlbindings</url>
<organization>
<name>Microsoft Azure</name>
<url>https://azure.microsoft.com</url>
</organization>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<licenses>
<license>
<name>MIT License</name>
<url>https://opensource.org/licenses/MIT</url>
<distribution>repo</distribution>
</license>
</licenses>

<scm>
<connection>scm:git:https://github.com/Azure/azure-functions-sql-extension</connection>
<developerConnection>scm:git:git@github.com:Azure/azure-functions-sql-extension</developerConnection>
<url>https://github.com/Azure/azure-functions-sql-extension</url>
<tag>HEAD</tag>
</scm>

<developers>
<developer>
<id>LucyZhang</id>
<name>Lucy Zhang</name>
<email>luczhan@microsoft.com</email>
</developer>
</developers>

<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<name>Sonatype Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<uniqueVersion>true</uniqueVersion>
<layout>default</layout>
</snapshotRepository>
</distributionManagement>

<repositories>
<repository>
<id>maven.snapshots</id>
<name>Maven Central Snapshot Repository</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>com.microsoft.azure.functions</groupId>
<artifactId>azure-functions-java-library</artifactId>
<version>1.4.2</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc.version}</version>
<configuration>
<doclint>none</doclint>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/

package com.microsoft.azure.functions.sql.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;

import com.microsoft.azure.functions.annotation.CustomBinding;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@CustomBinding(direction = "in", name = "sql", type = "sql")
public @interface SQLInput {
/**
* Query string or name of stored procedure to be run.
*/
String commandText() default "";

/**
* Text or Stored Procedure.
*/
String commandType() default "";

/**
* Parameters to the query or stored procedure. This string must follow the format
* "@param1=param1,@param2=param2" where @param1 is the name of the parameter and
* param1 is the parameter value.
*/
String parameters() default "";

/**
* Setting name for SQL connection string.
*/
String connectionStringSetting() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/

package com.microsoft.azure.functions.sql.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;

import com.microsoft.azure.functions.annotation.CustomBinding;


@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@CustomBinding(direction = "out", name = "sql", type = "sql")
public @interface SQLOutput {
/**
* Name of the table to upsert data to.
*/
String commandText() default "";

/**
* Setting name for SQL connection string.
*/
String connectionStringSetting() default "";
}
2 changes: 1 addition & 1 deletion performance/SqlTriggerBindingPerformance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ await this.WaitForProductChanges(
() => { this.InsertProducts(1, count); return Task.CompletedTask; },
id => $"Product {id}",
id => id * 100,
GetBatchProcessingTimeout(1, count));
this.GetBatchProcessingTimeout(1, count));
}

[IterationCleanup]
Expand Down
7 changes: 7 additions & 0 deletions samples/samples-csharp/Common/Product.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,11 @@ public class ProductName
{
public string Name { get; set; }
}

public class ProductWithDefaultPK
{
public string Name { get; set; }

public int Cost { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE [ProductsWithDefaultPK] (
[ProductGuid] [uniqueidentifier] PRIMARY KEY NOT NULL DEFAULT(newsequentialid()),
[Name] [nvarchar](100) NULL,
[Cost] [int] NULL
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common;

namespace Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples
{

public static class AddProductWithDefaultPK
{
/// <summary>
/// This shows an example of a SQL Output binding where the target table has a default primary key
/// of type uniqueidentifier and the column is not included in the output object. A new row will
/// be inserted and the uniqueidentifier will be generated by the engine.
/// </summary>
/// <param name="req">The original request that triggered the function</param>
/// <param name="product">The created ProductWithDefaultPK object</param>
/// <returns>The CreatedResult containing the new object that was inserted</returns>
[FunctionName(nameof(AddProductWithDefaultPK))]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "addproductwithdefaultpk")]
[FromBody] ProductWithDefaultPK product,
[Sql("dbo.ProductsWithDefaultPK", ConnectionStringSetting = "SqlConnectionString")] out ProductWithDefaultPK output)
{
output = product;
return new CreatedResult($"/api/addproductwithdefaultpk", output);
}
}
}
Loading