diff --git a/extensions/sql-migration/src/api/sqlUtils.ts b/extensions/sql-migration/src/api/sqlUtils.ts index 2bb90c3bc2..f41071896e 100644 --- a/extensions/sql-migration/src/api/sqlUtils.ts +++ b/extensions/sql-migration/src/api/sqlUtils.ts @@ -8,6 +8,7 @@ import { azureResource } from 'azurecore'; import { AzureSqlDatabase, AzureSqlDatabaseServer } from './azure'; import { generateGuid } from './utils'; import * as utils from '../api/utils'; +import { TelemetryAction, TelemetryViews, logError } from '../telemtery'; const query_database_tables_sql = ` SELECT @@ -43,7 +44,21 @@ const query_target_databases_sql = ` AND is_distributor <> 1 ORDER BY db.name;`; -export const excludeDatabses: string[] = [ +const query_databases_with_size = ` + WITH + db_size + AS + ( + SELECT database_id, CAST(SUM(size) * 8.0 / 1024 AS INTEGER) size + FROM sys.master_files with (nolock) + GROUP BY database_id + ) + SELECT name, state_desc AS state, db_size.size + FROM sys.databases with (nolock) LEFT JOIN db_size ON sys.databases.database_id = db_size.database_id + WHERE sys.databases.state = 0 + `; + +export const excludeDatabases: string[] = [ 'master', 'tempdb', 'msdb', @@ -263,7 +278,7 @@ export async function collectAzureTargetDatabases( targetServerName)); } return databaseList.filter( - database => !excludeDatabses.includes(database.name)) ?? []; + database => !excludeDatabases.includes(database.name)) ?? []; } export function getSqlString(value: azdata.DbCellValue): string { @@ -277,3 +292,30 @@ export function getSqlNumber(value: azdata.DbCellValue): number { export function getSqlBoolean(value: azdata.DbCellValue): boolean { return value.isNull ? false : value.displayValue === '1'; } + +export async function getDatabasesList(connectionProfile: azdata.connection.ConnectionProfile): Promise { + const ownerUri = await azdata.connection.getUriForConnection(connectionProfile.connectionId); + const queryProvider = azdata.dataprotocol.getProvider( + connectionProfile.providerId, + azdata.DataProviderType.QueryProvider); + + try { + const queryResult = await queryProvider.runQueryAndReturn(ownerUri, query_databases_with_size); + + const result = queryResult.rows.map(row => { + return { + options: { + name: getSqlString(row[0]), + state: getSqlString(row[1]), + sizeInMB: getSqlString(row[2]), + } + }; + }) ?? []; + + return result; + } catch (error) { + logError(TelemetryViews.Utils, TelemetryAction.GetDatabasesListFailed, error); + + return []; + } +} diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index 0eb66457bb..a6b93b59f4 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -934,7 +934,6 @@ export const ENTER_YOUR_SQL_CREDS = localize('sql.migration.enter.your.sql.cred' export const SERVER = localize('sql.migration.server', "Server"); export const USERNAME = localize('sql.migration.username', "User name"); export const SIZE = localize('sql.migration.size', "Size (MB)"); -export const LAST_BACKUP = localize('sql.migration.last.backup', "Last backup"); export const DATABASE_MIGRATE_TEXT = localize('sql.migrate.text', "Select the databases that you want to migrate to Azure SQL."); export const OFFLINE_CAPS = localize('sql.migration.offline.caps', "OFFLINE"); export const SELECT_DATABASE_TO_CONTINUE = localize('sql.migration.select.database.to.continue', "Please select 1 or more databases to assess for migration"); diff --git a/extensions/sql-migration/src/models/stateMachine.ts b/extensions/sql-migration/src/models/stateMachine.ts index 9fefe28543..0b98a2fb17 100644 --- a/extensions/sql-migration/src/models/stateMachine.ts +++ b/extensions/sql-migration/src/models/stateMachine.ts @@ -14,7 +14,7 @@ import { v4 as uuidv4 } from 'uuid'; import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews, logError } from '../telemtery'; import { hashString, deepClone } from '../api/utils'; import { SKURecommendationPage } from '../wizard/skuRecommendationPage'; -import { excludeDatabses, TargetDatabaseInfo } from '../api/sqlUtils'; +import { excludeDatabases, TargetDatabaseInfo } from '../api/sqlUtils'; const localize = nls.loadMessageBundle(); export enum State { @@ -294,7 +294,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { } public async getDatabases(): Promise { const temp = await azdata.connection.listDatabases(this.sourceConnectionId); - const finalResult = temp.filter((name) => !excludeDatabses.includes(name)); + const finalResult = temp.filter((name) => !excludeDatabases.includes(name)); return finalResult; } public hasRecommendedDatabaseListChanged(): boolean { diff --git a/extensions/sql-migration/src/telemtery.ts b/extensions/sql-migration/src/telemtery.ts index b0e58c9d15..a0847741eb 100644 --- a/extensions/sql-migration/src/telemtery.ts +++ b/extensions/sql-migration/src/telemtery.ts @@ -60,7 +60,8 @@ export enum TelemetryAction { GetDBSkuRecommendation = 'GetDBSkuRecommendation', GetInstanceRequirements = 'GetInstanceRequirements', StartDataCollection = 'StartDataCollection', - StopDataCollection = 'StopDataCollection' + StopDataCollection = 'StopDataCollection', + GetDatabasesListFailed = 'GetDatabasesListFailed' } export function logError(telemetryView: TelemetryViews, err: string, error: any): void { diff --git a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts index 8209165aba..23697b67d7 100644 --- a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts +++ b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts @@ -11,6 +11,7 @@ import * as constants from '../constants/strings'; import { debounce } from '../api/utils'; import * as styles from '../constants/styles'; import { IconPathHelper } from '../constants/iconPathHelper'; +import { getDatabasesList, excludeDatabases } from '../api/sqlUtils'; export class DatabaseSelectorPage extends MigrationWizardPage { private _view!: azdata.ModelView; @@ -119,8 +120,7 @@ export class DatabaseSelectorPage extends MigrationWizardPage { const searchText = value?.toLowerCase(); return row[2]?.toLowerCase()?.indexOf(searchText) > -1 // database name || row[3]?.toLowerCase()?.indexOf(searchText) > -1 // state - || row[4]?.toLowerCase()?.indexOf(searchText) > -1 // size - || row[5]?.toLowerCase()?.indexOf(searchText) > -1; // last backup date + || row[4]?.toLowerCase()?.indexOf(searchText) > -1; // size }); } @@ -208,14 +208,6 @@ export class DatabaseSelectorPage extends MigrationWizardPage { cssClass: cssClass, headerCssClass: cssClass, }, - { - name: constants.LAST_BACKUP, - value: 'lastBackup', - type: azdata.ColumnType.text, - width: 130, - cssClass: cssClass, - headerCssClass: cssClass, - }, ] }).component(); @@ -242,21 +234,10 @@ export class DatabaseSelectorPage extends MigrationWizardPage { } private async _loadDatabaseList(stateMachine: MigrationStateModel, selectedDatabases: string[]): Promise { - const providerId = (await stateMachine.getSourceConnectionProfile()).providerId; - const metaDataService = azdata.dataprotocol.getProvider( - providerId, - azdata.DataProviderType.MetadataProvider); - const ownerUri = await azdata.connection.getUriForConnection( - stateMachine.sourceConnectionId); - const excludeDbs: string[] = [ - 'master', - 'tempdb', - 'msdb', - 'model' - ]; - const databaseList = (await metaDataService - .getDatabases(ownerUri)) - .filter(database => !excludeDbs.includes(database.options.name)) + const allDatabases = (await getDatabasesList(await stateMachine.getSourceConnectionProfile())); + + const databaseList = allDatabases + .filter(database => !excludeDatabases.includes(database.options.name)) || []; databaseList.sort((a, b) => a.options.name.localeCompare(b.options.name)); @@ -274,7 +255,6 @@ export class DatabaseSelectorPage extends MigrationWizardPage { databaseName, database.options.state, database.options.sizeInMB, - database.options.lastBackup, ]; }) || []; }