An alternative to UBB.threads
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
10 KiB

// Copyright (c) 2009 Sun Microsystems, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as published by
// the Free Software Foundation
//
// There are special exceptions to the terms and conditions of the GPL
// as it is applied to this software. View the full text of the
// exception in file EXCEPTIONS in the directory of this software
// distribution.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Text;
using MySql.Data.Types;
using System.Diagnostics;
using System.Collections.Generic;
using MySql.Data.MySqlClient.Properties;
using System.Threading;
using MySql.Data.Common;
namespace MySql.Data.MySqlClient
{
internal class TracingDriver : Driver
{
private static long driverCounter;
private long driverId;
private ResultSet activeResult;
private int rowSizeInBytes;
public TracingDriver(MySqlConnectionStringBuilder settings)
: base(settings)
{
driverId = Interlocked.Increment(ref driverCounter);
}
public override void Open()
{
base.Open();
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.ConnectionOpened,
Resources.TraceOpenConnection, driverId, Settings.ConnectionString, ThreadID);
}
public override void Close()
{
base.Close();
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.ConnectionClosed,
Resources.TraceCloseConnection, driverId);
}
public override void SendQuery(MySqlPacket p)
{
rowSizeInBytes = 0;
string cmdText = Encoding.GetString(p.Buffer, 5, p.Length - 5);
string normalized_query = null;
if (cmdText.Length > 300)
{
cmdText = cmdText.Substring(0, 300);
QueryNormalizer normalizer = new QueryNormalizer();
normalized_query = normalizer.Normalize(cmdText);
}
base.SendQuery(p);
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.QueryOpened,
Resources.TraceQueryOpened, driverId, ThreadID, cmdText);
if (normalized_query != null)
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.QueryNormalized,
Resources.TraceQueryNormalized, driverId, ThreadID, normalized_query);
}
protected override int GetResult(int statementId, ref int affectedRows, ref int insertedId)
{
try
{
int fieldCount = base.GetResult(statementId, ref affectedRows, ref insertedId);
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.ResultOpened,
Resources.TraceResult, driverId, fieldCount, affectedRows, insertedId);
return fieldCount;
}
catch (MySqlException ex)
{
// we got an error so we report it
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.Error,
Resources.TraceOpenResultError, driverId, ex.Number, ex.Message);
throw ex;
}
}
public override ResultSet NextResult(int statementId)
{
// first let's see if we already have a resultset on this statementId
if (activeResult != null)
{
//oldRS = activeResults[statementId];
if (Settings.UseUsageAdvisor)
ReportUsageAdvisorWarnings(statementId, activeResult);
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.ResultClosed,
Resources.TraceResultClosed, driverId, activeResult.TotalRows, activeResult.SkippedRows,
rowSizeInBytes);
rowSizeInBytes = 0;
activeResult = null;
}
activeResult = base.NextResult(statementId);
return activeResult;
}
public override int PrepareStatement(string sql, ref MySqlField[] parameters)
{
int statementId = base.PrepareStatement(sql, ref parameters);
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.StatementPrepared,
Resources.TraceStatementPrepared, driverId, sql, statementId);
return statementId;
}
public override void CloseStatement(int id)
{
base.CloseStatement(id);
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.StatementClosed,
Resources.TraceStatementClosed, driverId, id);
}
public override void SetDatabase(string dbName)
{
base.SetDatabase(dbName);
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.NonQuery,
Resources.TraceSetDatabase, driverId, dbName);
}
public override void ExecuteStatement(MySqlPacket packetToExecute)
{
base.ExecuteStatement(packetToExecute);
int pos = packetToExecute.Position;
packetToExecute.Position = 1;
int statementId = packetToExecute.ReadInteger(4);
packetToExecute.Position = pos;
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.StatementExecuted,
Resources.TraceStatementExecuted, driverId, statementId, ThreadID);
}
public override bool FetchDataRow(int statementId, int columns)
{
try
{
bool b = base.FetchDataRow(statementId, columns);
if (b)
rowSizeInBytes += (handler as NativeDriver).Packet.Length;
return b;
}
catch (MySqlException ex)
{
MySqlTrace.TraceEvent(TraceEventType.Error, MySqlTraceEventType.Error,
Resources.TraceFetchError, driverId, ex.Number, ex.Message);
throw ex;
}
}
public override void CloseQuery(MySqlConnection connection, int statementId)
{
base.CloseQuery(connection, statementId);
MySqlTrace.TraceEvent(TraceEventType.Information, MySqlTraceEventType.QueryClosed,
Resources.TraceQueryDone, driverId);
}
public override List<MySqlError> ReportWarnings(MySqlConnection connection)
{
List<MySqlError> warnings = base.ReportWarnings(connection);
foreach (MySqlError warning in warnings)
MySqlTrace.TraceEvent(TraceEventType.Warning, MySqlTraceEventType.Warning,
Resources.TraceWarning, driverId, warning.Level, warning.Code, warning.Message);
return warnings;
}
private bool AllFieldsAccessed(ResultSet rs)
{
if (rs.Fields == null || rs.Fields.Length == 0) return true;
for (int i = 0; i < rs.Fields.Length; i++)
if (!rs.FieldRead(i)) return false;
return true;
}
private void ReportUsageAdvisorWarnings(int statementId, ResultSet rs)
{
if (!Settings.UseUsageAdvisor) return;
if (HasStatus(ServerStatusFlags.NoIndex))
MySqlTrace.TraceEvent(TraceEventType.Warning, MySqlTraceEventType.UsageAdvisorWarning,
Resources.TraceUAWarningNoIndex, driverId, UsageAdvisorWarningFlags.NoIndex);
else if (HasStatus(ServerStatusFlags.BadIndex))
MySqlTrace.TraceEvent(TraceEventType.Warning, MySqlTraceEventType.UsageAdvisorWarning,
Resources.TraceUAWarningBadIndex, driverId, UsageAdvisorWarningFlags.BadIndex);
// report abandoned rows
if (rs.SkippedRows > 0)
MySqlTrace.TraceEvent(TraceEventType.Warning, MySqlTraceEventType.UsageAdvisorWarning,
Resources.TraceUAWarningSkippedRows, driverId, UsageAdvisorWarningFlags.SkippedRows, rs.SkippedRows);
// report not all fields accessed
if (!AllFieldsAccessed(rs))
{
StringBuilder notAccessed = new StringBuilder("");
string delimiter = "";
for (int i = 0; i < rs.Size; i++)
if (!rs.FieldRead(i))
{
notAccessed.AppendFormat("{0}{1}", delimiter, rs.Fields[i].ColumnName);
delimiter = ",";
}
MySqlTrace.TraceEvent(TraceEventType.Warning, MySqlTraceEventType.UsageAdvisorWarning,
Resources.TraceUAWarningSkippedColumns, driverId, UsageAdvisorWarningFlags.SkippedColumns,
notAccessed.ToString());
}
// report type conversions if any
if (rs.Fields != null)
{
foreach (MySqlField f in rs.Fields)
{
StringBuilder s = new StringBuilder();
string delimiter = "";
foreach (Type t in f.TypeConversions)
{
s.AppendFormat("{0}{1}", delimiter, t.Name);
delimiter = ",";
}
if (s.Length > 0)
MySqlTrace.TraceEvent(TraceEventType.Warning, MySqlTraceEventType.UsageAdvisorWarning,
Resources.TraceUAWarningFieldConversion, driverId, UsageAdvisorWarningFlags.FieldConversion,
f.ColumnName, s.ToString());
}
}
}
}
}