Skip to content

Commit f8203ea

Browse files
authored
Prevent agent from unsupported USE commands on Azure SQL DBs (#17448)
* Do not run USE commands on Azure SQL DB
1 parent 1897699 commit f8203ea

File tree

3 files changed

+41
-31
lines changed

3 files changed

+41
-31
lines changed

sqlserver/changelog.d/17448.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevent agent from unsupported USE commands on Azure SQL DBs

sqlserver/datadog_checks/sqlserver/metrics.py

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from datadog_checks.base.errors import CheckException
1414
from datadog_checks.base.utils.time import get_precise_time
1515

16-
from .utils import construct_use_statement
16+
from .utils import construct_use_statement, is_azure_sql_database
1717

1818
# Queries
1919
ALL_INSTANCES = 'ALL'
@@ -80,7 +80,7 @@ def _fetch_generic_values(cls, cursor, counters_list, logger):
8080
return rows, columns
8181

8282
@classmethod
83-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
83+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
8484
raise NotImplementedError
8585

8686
def fetch_metric(self, rows, columns, values_cache=None):
@@ -98,7 +98,7 @@ class SqlSimpleMetric(BaseSqlServerMetric):
9898
OPERATION_NAME = 'simple_metrics'
9999

100100
@classmethod
101-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
101+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
102102
return cls._fetch_generic_values(cursor, counters_list, logger)
103103

104104
def fetch_metric(self, rows, columns, values_cache=None):
@@ -136,7 +136,7 @@ class SqlFractionMetric(BaseSqlServerMetric):
136136
OPERATION_NAME = 'fraction_metrics'
137137

138138
@classmethod
139-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
139+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
140140
placeholders = ', '.join('?' for _ in counters_list)
141141
query = cls.QUERY_BASE.format(placeholders=placeholders)
142142

@@ -257,7 +257,7 @@ class SqlOsWaitStat(BaseSqlServerMetric):
257257
OPERATION_NAME = 'os_wait_stat_metric'
258258

259259
@classmethod
260-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
260+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
261261
return cls._fetch_generic_values(cursor, counters_list, logger)
262262

263263
def fetch_metric(self, rows, columns, values_cache=None):
@@ -289,7 +289,7 @@ class SqlIoVirtualFileStat(BaseSqlServerMetric):
289289
OPERATION_NAME = 'io_virtual_file_stats_metrics'
290290

291291
@classmethod
292-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
292+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
293293
# since we want the database name we need to update the SQL query at runtime with our custom columns
294294
# multiple formats on a string are harmless
295295
extra_cols = ', '.join(col for col in counters_list)
@@ -349,7 +349,7 @@ class SqlOsMemoryClerksStat(BaseSqlServerMetric):
349349
OPERATION_NAME = 'os_memory_clerks_stat_metrics'
350350

351351
@classmethod
352-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
352+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
353353
return cls._fetch_generic_values(cursor, counters_list, logger)
354354

355355
def fetch_metric(self, rows, columns, values_cache=None):
@@ -381,7 +381,7 @@ class SqlOsSchedulers(BaseSqlServerMetric):
381381
OPERATION_NAME = 'os_schedulers_metrics'
382382

383383
@classmethod
384-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
384+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
385385
return cls._fetch_generic_values(cursor, None, logger)
386386

387387
def fetch_metric(self, rows, columns, values_cache=None):
@@ -418,7 +418,7 @@ class SqlOsTasks(BaseSqlServerMetric):
418418
OPERATION_NAME = 'os_tasks_metrics'
419419

420420
@classmethod
421-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
421+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
422422
return cls._fetch_generic_values(cursor, None, logger)
423423

424424
def fetch_metric(self, rows, columns, values_cache=None):
@@ -451,7 +451,7 @@ class SqlMasterDatabaseFileStats(BaseSqlServerMetric):
451451
OPERATION_NAME = 'master_database_file_stats_metrics'
452452

453453
@classmethod
454-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
454+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
455455
return cls._fetch_generic_values(cursor, None, logger)
456456

457457
def fetch_metric(self, rows, columns, values_cache=None):
@@ -502,7 +502,7 @@ def __init__(self, cfg_instance, base_name, report_function, column, logger):
502502
super(SqlDatabaseFileStats, self).__init__(cfg_instance, base_name, report_function, column, logger)
503503

504504
@classmethod
505-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
505+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
506506
# special case since this table is specific to databases, need to run query for each database instance
507507
rows = []
508508
columns = []
@@ -519,8 +519,10 @@ def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
519519
# use statements need to be executed separate from select queries
520520
ctx = construct_use_statement(db)
521521
try:
522-
logger.debug("%s: changing cursor context via use statement: %s", cls.__name__, ctx)
523-
cursor.execute(ctx)
522+
# Azure SQL DB does not allow running the USE command
523+
if not is_azure_sql_database(engine_edition):
524+
logger.debug("%s: changing cursor context via use statement: %s", cls.__name__, ctx)
525+
cursor.execute(ctx)
524526
logger.debug("%s: fetch_all executing query: %s", cls.__name__, cls.QUERY_BASE)
525527
cursor.execute(cls.QUERY_BASE)
526528
data = cursor.fetchall()
@@ -546,9 +548,11 @@ def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
546548

547549
logger.debug("%s: received %d rows and %d columns for db %s", cls.__name__, len(data), len(columns), db)
548550

549-
# reset back to previous db
550-
logger.debug("%s: reverting cursor context via use statement to %s", cls.__name__, current_db)
551-
cursor.execute(construct_use_statement(current_db))
551+
# Azure SQL DB does not allow running the USE command
552+
if not is_azure_sql_database(engine_edition):
553+
# reset back to previous db
554+
logger.debug("%s: reverting cursor context via use statement to %s", cls.__name__, current_db)
555+
cursor.execute(construct_use_statement(current_db))
552556

553557
return rows, columns
554558

@@ -602,7 +606,7 @@ class SqlDatabaseStats(BaseSqlServerMetric):
602606
OPERATION_NAME = 'database_stats_metrics'
603607

604608
@classmethod
605-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
609+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
606610
return cls._fetch_generic_values(cursor, None, logger)
607611

608612
def fetch_metric(self, rows, columns, values_cache=None):
@@ -648,7 +652,7 @@ class SqlDatabaseBackup(BaseSqlServerMetric):
648652
OPERATION_NAME = 'database_backup_metrics'
649653

650654
@classmethod
651-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
655+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
652656
return cls._fetch_generic_values(cursor, None, logger)
653657

654658
def fetch_metric(self, rows, columns, values_cache=None):
@@ -702,7 +706,7 @@ def __init__(self, cfg_instance, base_name, report_function, column, logger):
702706
super(SqlDbFragmentation, self).__init__(cfg_instance, base_name, report_function, column, logger)
703707

704708
@classmethod
705-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
709+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
706710
# special case to limit this query to specific databases and monitor performance
707711
rows = []
708712
columns = []
@@ -712,17 +716,16 @@ def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
712716
logger.debug("%s: gathering fragmentation metrics for these databases: %s", cls.__name__, databases)
713717

714718
for db in databases:
715-
print(db)
716719
ctx = construct_use_statement(db)
717720
query = cls.QUERY_BASE.format(db=db)
718721
start = get_precise_time()
719722
try:
720-
logger.debug("%s: changing cursor context via use statement: %s", cls.__name__, ctx)
721-
cursor.execute(ctx)
723+
if not is_azure_sql_database(engine_edition):
724+
logger.debug("%s: changing cursor context via use statement: %s", cls.__name__, ctx)
725+
cursor.execute(ctx)
722726
logger.debug("%s: fetch_all executing query: %s", cls.__name__, query)
723727
cursor.execute(query)
724728
data = cursor.fetchall()
725-
print(query, data)
726729
except Exception as e:
727730
logger.warning("Error when trying to query db %s - skipping. Error: %s", db, e)
728731
continue
@@ -787,7 +790,7 @@ class SqlDbReplicaStates(BaseSqlServerMetric):
787790
OPERATION_NAME = 'db_replica_states_metrics'
788791

789792
@classmethod
790-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
793+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
791794
return cls._fetch_generic_values(cursor, None, logger)
792795

793796
def fetch_metric(self, rows, columns, values_cache=None):
@@ -842,7 +845,7 @@ class SqlAvailabilityGroups(BaseSqlServerMetric):
842845
OPERATION_NAME = 'availability_groups_metrics'
843846

844847
@classmethod
845-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
848+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
846849
return cls._fetch_generic_values(cursor, None, logger)
847850

848851
def fetch_metric(self, rows, columns, values_cache=None):
@@ -896,7 +899,7 @@ class SqlAvailabilityReplicas(BaseSqlServerMetric):
896899
OPERATION_NAME = 'availability_replicas_metrics'
897900

898901
@classmethod
899-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
902+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
900903
return cls._fetch_generic_values(cursor, None, logger)
901904

902905
def fetch_metric(self, rows, columns, values_cache=None):
@@ -973,7 +976,7 @@ class SqlDbFileSpaceUsage(BaseSqlServerMetric):
973976
OPERATION_NAME = 'db_file_space_usage_metrics'
974977

975978
@classmethod
976-
def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
979+
def fetch_all_values(cls, cursor, counters_list, logger, databases=None, engine_edition=None):
977980
rows = []
978981
columns = []
979982

@@ -987,8 +990,9 @@ def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
987990
ctx = construct_use_statement(db)
988991
start = get_precise_time()
989992
try:
990-
logger.debug("%s: changing cursor context via use statement: %s", cls.__name__, ctx)
991-
cursor.execute(ctx)
993+
if not is_azure_sql_database(engine_edition):
994+
logger.debug("%s: changing cursor context via use statement: %s", cls.__name__, ctx)
995+
cursor.execute(ctx)
992996
logger.debug("%s: fetch_all executing query: %s", cls.__name__, cls.QUERY_BASE)
993997
cursor.execute(cls.QUERY_BASE)
994998
data = cursor.fetchall()
@@ -1007,7 +1011,7 @@ def fetch_all_values(cls, cursor, counters_list, logger, databases=None):
10071011
logger.debug("%s: received %d rows for db %s, elapsed time: %.4f sec", cls.__name__, len(data), db, elapsed)
10081012

10091013
# reset back to previous db
1010-
if current_db:
1014+
if current_db and not is_azure_sql_database(engine_edition):
10111015
logger.debug("%s: reverting cursor context via use statement to %s", cls.__name__, current_db)
10121016
cursor.execute(construct_use_statement(current_db))
10131017

sqlserver/datadog_checks/sqlserver/sqlserver.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,7 @@ def collect_metrics(self):
868868
self._make_metric_list_to_collect(self._config.custom_metrics)
869869

870870
instance_results = {}
871+
engine_edition = self.static_info_cache.get(STATIC_INFO_ENGINE_EDITION, "")
871872
# Execute the `fetch_all` operations first to minimize the database calls
872873
for cls, metric_names in six.iteritems(self.instance_per_type_metrics):
873874
if not metric_names:
@@ -880,7 +881,11 @@ def collect_metrics(self):
880881
metric_cls = getattr(metrics, cls)
881882
with tracked_query(self, operation=metric_cls.OPERATION_NAME):
882883
rows, cols = metric_cls.fetch_all_values(
883-
cursor, list(metric_names), self.log, databases=db_names
884+
cursor,
885+
list(metric_names),
886+
self.log,
887+
databases=db_names,
888+
engine_edition=engine_edition,
884889
)
885890
except Exception as e:
886891
self.log.error("Error running `fetch_all` for metrics %s - skipping. Error: %s", cls, e)

0 commit comments

Comments
 (0)