mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 17:22:48 -05:00
Expose default database collation through 'sql-database-projects' extension API (#15538)
* Expose default database collation through 'sql-database-projects' extension API. For the purpose of schema conversion we would need to know whether target database is configured as CI or CS. This will be used to produce a warning, if we detect a case-sensitive identifier, but database is configured as CI. In order to support this scenario we need to access `<DefaultCollation/>` property of the project. This change adds new method to the `ISqlProject` interface that allows to read the value of the project property. There already was similar method for the SQL version/platform (`<DSP/>` property) and while working on the change, I took an opportunity to refactor the way project properties are extracted from the XML. Original code was hardcoded in the `getProjectTargetVersion` and I extracted it into separate `evaluateProjectPropertyValue` helper, that can be used in the future by any getter or access method that is supposed to return a value of the single property. This also allows us to improve the way properties are retrieved from the XML. Today the logic is very rudimentary - we read the first matching XML element with the required name. This is not correct as it does not verify the parent to be `<PropertyGroup/>`, neither it evaluates the `Condition` attributes nor property value itself. I did not invest in this, but added a TODO there explaining that the method may not perform as expected. After the helper method was added, I updated the existing `getProjectTargetVersion` method to leverage it. The only complication here was the error throwing logic, as it was using custom error message. I preserved that, as there were tests verifying it already. For the new accessor method I did not introduce a special error message and rely on generic one I defined for use within the helper method. Additionally, for the collation we return default value of `SQL_Latin1_General_CP1_CI_AS`, if project does not have the property defined. This is what SSDT for Visual Studio shows in the UI when property is missing and I decided to align with that. Finally, I added tests for both - original `getProjectTargetVersion` and new collation extraction method to make sure they work as expected. While working on the tests, I've noticed complaints that some rejected promises were not awaited. I added missing `await`s.
This commit is contained in:
@@ -516,18 +516,27 @@ export class Project implements ISqlProject {
|
||||
}
|
||||
|
||||
public getProjectTargetVersion(): string {
|
||||
// check for invalid DSP
|
||||
if (this.projFileXmlDoc.getElementsByTagName(constants.DSP).length !== 1 || this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes.length !== 1) {
|
||||
let dsp: string | undefined;
|
||||
|
||||
try {
|
||||
dsp = this.evaluateProjectPropertyValue(constants.DSP);
|
||||
}
|
||||
catch {
|
||||
// We will throw specialized error instead
|
||||
}
|
||||
|
||||
// Check if DSP is missing or invalid
|
||||
if (!dsp) {
|
||||
throw new Error(constants.invalidDataSchemaProvider);
|
||||
}
|
||||
|
||||
let dsp: string = this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes[0].data;
|
||||
|
||||
// get version from dsp, which is a string like Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider
|
||||
// remove part before the number
|
||||
let version: any = dsp.substring(constants.MicrosoftDatatoolsSchemaSqlSql.length);
|
||||
// remove DatabaseSchemaProvider
|
||||
version = version.substring(0, version.length - constants.databaseSchemaProvider.length);
|
||||
// Remove prefix and suffix to only get the actual version number/name. For the example above the result
|
||||
// should be just '130'.
|
||||
const version =
|
||||
dsp.substring(
|
||||
constants.MicrosoftDatatoolsSchemaSqlSql.length,
|
||||
dsp.length - constants.databaseSchemaProvider.length);
|
||||
|
||||
// make sure version is valid
|
||||
if (!Array.from(constants.targetPlatformToVersion.values()).includes(version)) {
|
||||
@@ -537,6 +546,15 @@ export class Project implements ISqlProject {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default database collation set in the project.
|
||||
*
|
||||
* @returns Default collation for the database set in the project.
|
||||
*/
|
||||
public getDatabaseDefaultCollation(): string {
|
||||
return this.evaluateProjectPropertyValue(constants.DefaultCollationProperty, constants.DefaultCollation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds reference to a dacpac to the project
|
||||
* @param uri Uri of the dacpac
|
||||
@@ -1039,6 +1057,52 @@ export class Project implements ISqlProject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the value of the property item in the loaded project.
|
||||
*
|
||||
* @param propertyName Name of the property item to evaluate.
|
||||
* @returns Value of the property or `undefined`, if property is missing.
|
||||
*/
|
||||
private evaluateProjectPropertyValue(propertyName: string): string | undefined;
|
||||
|
||||
/**
|
||||
* Evaluates the value of the property item in the loaded project.
|
||||
*
|
||||
* @param propertyName Name of the property item to evaluate.
|
||||
* @param defaultValue Default value to return, if property is not set.
|
||||
* @returns Value of the property or `defaultValue`, if property is missing.
|
||||
*/
|
||||
private evaluateProjectPropertyValue(propertyName: string, defaultValue: string): string;
|
||||
|
||||
/**
|
||||
* Evaluates the value of the property item in the loaded project.
|
||||
*
|
||||
* @param propertyName Name of the property item to evaluate.
|
||||
* @param defaultValue Default value to return, if property is not set.
|
||||
* @returns Value of the property or `defaultValue`, if property is missing.
|
||||
*/
|
||||
private evaluateProjectPropertyValue(propertyName: string, defaultValue?: string): string | undefined {
|
||||
// TODO: Currently we simply read the value of the first matching element. The code should be updated to:
|
||||
// 1) Narrow it down to items under <PropertyGroup> only
|
||||
// 2) Respect the `Condition` attribute on group and property itself
|
||||
// 3) Evaluate any expressions within the property value
|
||||
|
||||
// Check if property is set in the project
|
||||
const propertyElements = this.projFileXmlDoc.getElementsByTagName(propertyName);
|
||||
if (propertyElements.length === 0) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// Try to extract the value from the first matching element
|
||||
const firstPropertyElement = propertyElements[0];
|
||||
if (firstPropertyElement.childNodes.length !== 1) {
|
||||
// Property items are expected to have simple string content
|
||||
throw new Error(constants.invalidProjectPropertyValue(propertyName));
|
||||
}
|
||||
|
||||
return firstPropertyElement.childNodes[0].data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user