Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sql fix #314

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
19 changes: 2 additions & 17 deletions Source/Applications/Wave Demo Apps/UpdateWAVMetaData/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,9 @@ static int Main(string[] args)
Guid nodeID = systemSettings["NodeID"].ValueAs<Guid>();
bool useMemoryCache = systemSettings["UseMemoryCache"].ValueAsBoolean(false);
string connectionString = systemSettings["ConnectionString"].Value;
string nodeIDQueryString = null;
string parameterizedQuery;
int protocolID, signalTypePMID, signalTypePAID;

// Define guid with query string delimiters according to database needs
Dictionary<string, string> settings = connectionString.ParseKeyValuePairs();
string setting;

if (settings.TryGetValue("Provider", out setting))
{
// Check if provider is for Access since it uses braces as Guid delimiters
if (setting.StartsWith("Microsoft.Jet.OLEDB", StringComparison.OrdinalIgnoreCase))
nodeIDQueryString = "{" + nodeID + "}";
}

if (string.IsNullOrWhiteSpace(nodeIDQueryString))
nodeIDQueryString = "'" + nodeID + "'";

using (AdoDataConnection database = new AdoDataConnection("systemSettings"))
{
IDbConnection connection = database.Connection;
Expand Down Expand Up @@ -109,12 +94,12 @@ static int Main(string[] args)
if (Convert.ToInt32(connection.ExecuteScalar(database.ParameterizedQueryString("SELECT COUNT(*) FROM Device WHERE Acronym = {0}", "acronym"), acronym)) == 0)
{
parameterizedQuery = database.ParameterizedQueryString("INSERT INTO Device(NodeID, Acronym, Name, ProtocolID, FramesPerSecond, " +
"MeasurementReportingInterval, ConnectionString, Enabled) VALUES(" + nodeIDQueryString + ", {0}, {1}, {2}, {3}, {4}, {5}, {6})",
"MeasurementReportingInterval, ConnectionString, Enabled) VALUES({7}, {0}, {1}, {2}, {3}, {4}, {5}, {6})",
"acronym", "name", "protocolID", "framesPerSecond", "measurementReportingInterval",
"connectionString", "enabled");
Comment on lines +97 to 99
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to also include a name for parameter 7. Probably, you should add 7 at the end and insert the name before acronym like so...

Suggested change
"MeasurementReportingInterval, ConnectionString, Enabled) VALUES({7}, {0}, {1}, {2}, {3}, {4}, {5}, {6})",
"acronym", "name", "protocolID", "framesPerSecond", "measurementReportingInterval",
"connectionString", "enabled");
"MeasurementReportingInterval, ConnectionString, Enabled) VALUES({0}, {1}, {2}, {3}, {4}, {5}, {6}, {7})",
"nodeID", "acronym", "name", "protocolID", "framesPerSecond", "measurementReportingInterval",
"connectionString", "enabled");


// Insert new device record
connection.ExecuteNonQuery(parameterizedQuery, acronym, name, protocolID, sourceWave.SampleRate, 1000000, $"wavFileName={FilePath.GetAbsolutePath(sourceFileName)}; connectOnDemand=true; outputSourceIDs={acronym}; memoryCache={useMemoryCache}", database.Bool(true));
connection.ExecuteNonQuery(parameterizedQuery, acronym, name, protocolID, sourceWave.SampleRate, 1000000, $"wavFileName={FilePath.GetAbsolutePath(sourceFileName)}; connectOnDemand=true; outputSourceIDs={acronym}; memoryCache={useMemoryCache}", database.Bool(true), nodeID);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This old API for parameterized queries isn't as clever about matching arguments to your format string. Even if you use {7} as the first parameter in the query string, nodeID has to be the first argument to ExecuteNonQuery(). Also, you need database.Guid() or else it won't work for SQLite, PostgreSQL, or Oracle.

Suggested change
connection.ExecuteNonQuery(parameterizedQuery, acronym, name, protocolID, sourceWave.SampleRate, 1000000, $"wavFileName={FilePath.GetAbsolutePath(sourceFileName)}; connectOnDemand=true; outputSourceIDs={acronym}; memoryCache={useMemoryCache}", database.Bool(true), nodeID);
connection.ExecuteNonQuery(parameterizedQuery, database.Guid(nodeID), acronym, name, protocolID, sourceWave.SampleRate, 1000000, $"wavFileName={FilePath.GetAbsolutePath(sourceFileName)}; connectOnDemand=true; outputSourceIDs={acronym}; memoryCache={useMemoryCache}", database.Bool(true));

int deviceID = Convert.ToInt32(connection.ExecuteScalar(database.ParameterizedQueryString("SELECT ID FROM Device WHERE Acronym = {0}", "acronym"), acronym));
string pointTag;
int lastPhasorIndex = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ public override void Initialize()
{
// Load any newly defined devices into the statistics device table
TableOperations<Device> deviceTable = new(statConnection);
DataRow[] devices = gsfConnection.RetrieveData($"SELECT * FROM Device WHERE IsConcentrator = 0 AND AccessID <> {DeviceGroupAccessID}").Select();
DataRow[] devices = gsfConnection.RetrieveData("SELECT * FROM Device WHERE IsConcentrator = 0 AND AccessID <> {0}", DeviceGroupAccessID).Select();

foreach (DataRow device in devices)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,14 +415,16 @@ private void LoadAlarmStates(bool reload = false)
m_alarmStateIDs[alarmStateRecord.ID] = alarmState;
}

// Define SQL expression for direct connect and parent devices or all direct connect and child devices
string deviceSQL = TargetParentDevices ?
"SELECT * FROM Device WHERE (IsConcentrator != 0 OR ParentID IS NULL) AND ID NOT IN (SELECT DeviceID FROM AlarmDevice)" :
$"SELECT * FROM Device WHERE IsConcentrator = 0 AND AccessID <> {DeviceGroupAccessID} AND ID NOT IN (SELECT DeviceID FROM AlarmDevice)";

// Load any newly defined devices into the alarm device table
TableOperations<AlarmDevice> alarmDeviceTable = new(connection);
DataRow[] newDevices = connection.RetrieveData(deviceSQL).Select();
DataRow[] newDevices;

// Define SQL expression for direct connect and parent devices or all direct connect and child devices
if (TargetParentDevices)
newDevices = connection.RetrieveData("SELECT * FROM Device WHERE (IsConcentrator != 0 OR ParentID IS NULL) " +
"AND ID NOT IN (SELECT DeviceID FROM AlarmDevice)").Select();
else newDevices = connection.RetrieveData("SELECT * FROM Device WHERE IsConcentrator = 0 " +
"AND AccessID <> {0} AND ID NOT IN (SELECT DeviceID FROM AlarmDevice)", DeviceGroupAccessID).Select();

foreach (DataRow newDevice in newDevices)
{
Expand Down Expand Up @@ -451,7 +453,9 @@ private void LoadAlarmStates(bool reload = false)
{
// Querying from MeasurementDetail because we also want to include disabled device measurements
string measurementSQL = TargetParentDevices ?
"SELECT MeasurementDetail.SignalID AS SignalID, MeasurementDetail.ID AS ID FROM MeasurementDetail INNER JOIN DeviceDetail ON MeasurementDetail.DeviceID = DeviceDetail.ID WHERE (DeviceDetail.Acronym = {0} OR DeviceDetail.ParentAcronym = {0}) AND MeasurementDetail.SignalAcronym = 'FREQ'" :
"SELECT MeasurementDetail.SignalID AS SignalID, MeasurementDetail.ID AS ID FROM MeasurementDetail " +
"INNER JOIN DeviceDetail ON MeasurementDetail.DeviceID = DeviceDetail.ID " +
"WHERE (DeviceDetail.Acronym = {0} OR DeviceDetail.ParentAcronym = {0}) AND MeasurementDetail.SignalAcronym = 'FREQ'" :
"SELECT SignalID, ID FROM MeasurementDetail WHERE DeviceAcronym = {0} AND SignalAcronym = 'FREQ'";

DataTable table = connection.RetrieveData(measurementSQL, metadata.ConvertField<string>("Acronym"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -939,11 +939,11 @@ private static void OptimizeLocalHistorianSettings(AdoDataConnection database, s
statusMessage("Optimizing settings for local historians...");

// Load the defined local system historians
IEnumerable<DataRow> historians = database.Connection.RetrieveData(database.AdapterType, $"SELECT AdapterName FROM RuntimeHistorian WHERE NodeID = {nodeIDQueryString} AND TypeName = 'HistorianAdapters.LocalOutputAdapter'").AsEnumerable();
IEnumerable<DataRow> readers = database.Connection.RetrieveData(database.AdapterType, $"SELECT * FROM CustomInputAdapter WHERE NodeID = {nodeIDQueryString} AND TypeName = 'HistorianAdapters.LocalInputAdapter'").AsEnumerable();
IEnumerable<DataRow> historians = database.Connection.RetrieveData(database.AdapterType, "SELECT AdapterName FROM RuntimeHistorian WHERE NodeID = {0} AND TypeName = 'HistorianAdapters.LocalOutputAdapter'", nodeIDQueryString).AsEnumerable();
IEnumerable<DataRow> readers = database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM CustomInputAdapter WHERE NodeID = {0} AND TypeName = 'HistorianAdapters.LocalInputAdapter'", nodeIDQueryString).AsEnumerable();

// Also check for local historian adapters loaded into CustomOutputAdapters
historians = historians.Concat(database.Connection.RetrieveData(database.AdapterType, $"SELECT AdapterName, ConnectionString FROM RuntimeCustomOutputAdapter WHERE NodeID = {nodeIDQueryString} AND TypeName = 'HistorianAdapters.LocalOutputAdapter'").AsEnumerable());
historians = historians.Concat(database.Connection.RetrieveData(database.AdapterType, "SELECT AdapterName, ConnectionString FROM RuntimeCustomOutputAdapter WHERE NodeID = {0} AND TypeName = 'HistorianAdapters.LocalOutputAdapter'", nodeIDQueryString).AsEnumerable());

string name, acronym, instanceName;

Expand Down Expand Up @@ -1202,11 +1202,11 @@ private static void OptimizeLocalHistorianSettings(AdoDataConnection database, s
private static void AddPIHistorianReaders(AdoDataConnection database, string nodeIDQueryString, Action<Exception> processException)
{
// Load the defined local PI historians
IEnumerable<DataRow> historians = database.Connection.RetrieveData(database.AdapterType, $"SELECT AdapterName, ConnectionString FROM RuntimeHistorian WHERE NodeID = {nodeIDQueryString} AND TypeName = 'PIAdapters.PIOutputAdapter'").AsEnumerable();
IEnumerable<DataRow> readers = database.Connection.RetrieveData(database.AdapterType, $"SELECT * FROM CustomInputAdapter WHERE NodeID = {nodeIDQueryString} AND TypeName = 'PIAdapters.PIPBInputAdapter'").AsEnumerable();
IEnumerable<DataRow> historians = database.Connection.RetrieveData(database.AdapterType, "SELECT AdapterName, ConnectionString FROM RuntimeHistorian WHERE NodeID = {0} AND TypeName = 'PIAdapters.PIOutputAdapter'", nodeIDQueryString).AsEnumerable();
IEnumerable<DataRow> readers = database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM CustomInputAdapter WHERE NodeID = {0} AND TypeName = 'PIAdapters.PIPBInputAdapter'", nodeIDQueryString).AsEnumerable();

// Also check for PI adapters loaded into CustomOutputAdapters
historians = historians.Concat(database.Connection.RetrieveData(database.AdapterType, $"SELECT AdapterName, ConnectionString FROM RuntimeCustomOutputAdapter WHERE NodeID = {nodeIDQueryString} AND TypeName = 'PIAdapters.PIOutputAdapter'").AsEnumerable());
historians = historians.Concat(database.Connection.RetrieveData(database.AdapterType, "SELECT AdapterName, ConnectionString FROM RuntimeCustomOutputAdapter WHERE NodeID = {0} AND TypeName = 'PIAdapters.PIOutputAdapter'", nodeIDQueryString).AsEnumerable());

// Make sure a temporal reader is defined for each OSI-PI historian
foreach (DataRow row in historians)
Expand Down
13 changes: 4 additions & 9 deletions Source/Libraries/Adapters/MySqlAdapters/MySqlOutputAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,12 @@ protected override void ProcessMeasurements(IMeasurement[] measurements)
foreach (IMeasurement measurement in measurements)
{
// Create the command string to insert the measurement as a record in the table.
StringBuilder commandString = new StringBuilder("INSERT INTO Measurement VALUES ('");
IDbCommand command = m_connection.CreateCommand();
command.Parameters.Add(measurement.ID);
command.Parameters.Add((long)measurement.Timestamp);
command.Parameters.Add(measurement.AdjustedValue);
Comment on lines +180 to +182
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a feeling this won't work. IDbCommand.Parameters returns a collection of type IDataParameterCollection which implements the non-generic IList interface. It's entirely possible that the MySQL implementation of the Add() method can convert any old value to an instance of IDbDataParameter, but I feel it's pretty unlikely. You'll need to use command.CreateParameter() and then set the ParameterName and Value fields for each parameter.


commandString.Append(measurement.ID);
commandString.Append("','");
commandString.Append((long)measurement.Timestamp);
commandString.Append("',");
commandString.Append(measurement.AdjustedValue);
commandString.Append(')');

command.CommandText = commandString.ToString();
command.CommandText = "INSERT INTO Measurement VALUES ({0}, {1}, {2})";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this. Format strings only work with AdoDataConnection. Otherwise, you need to use the SQL syntax for parameters. In MySQL, that'd be @parameterName.

command.ExecuteNonQuery();

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ private static void MeasurementDeviceAssociation(AdoDataConnection connection, s
int deviceID = connection.ExecuteScalar<int>($"SELECT ID FROM Device WHERE NodeID={nodeIDQueryString} AND Acronym={{0}}", deviceAcronym);

// Get measurements that should be associated with device ID but are not currently
IEnumerable<DataRow> measurements = connection.RetrieveData($"SELECT PointID FROM Measurement WHERE ({lookupExpression}) AND (DeviceID IS NULL OR DeviceID <> {{0}})", deviceID).AsEnumerable();
IEnumerable<DataRow> measurements = connection.RetrieveData("SELECT PointID FROM Measurement WHERE ({0}) AND (DeviceID IS NULL OR DeviceID <> {{1}})", lookupExpression, deviceID).AsEnumerable();

int associatedMeasurements = 0;

Expand Down Expand Up @@ -1487,7 +1487,8 @@ private static void PhasorDataSourceValidation(AdoDataConnection database, strin
int qualityFlagsSignalTypeID = Convert.ToInt32(database.Connection.ExecuteScalar("SELECT ID FROM SignalType WHERE Acronym='QUAL'"));

// Make sure one device quality flags measurement exists for each "connection" for devices that support time quality flags
foreach (DataRow device in database.Connection.RetrieveData(database.AdapterType, $"SELECT * FROM Device WHERE ((IsConcentrator = 0 AND ParentID IS NULL) OR IsConcentrator = 1) AND NodeID = {nodeIDQueryString} AND ProtocolID IN ({timeQualityProtocolIDs})").Rows)
foreach (DataRow device in database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM Device WHERE ((IsConcentrator = 0 AND ParentID IS NULL) " +
"OR IsConcentrator = 1) AND NodeID = {0} AND ProtocolID IN ({1})", nodeIDQueryString, timeQualityProtocolIDs).Rows)
{
Dictionary<string, string> connectionSettings = device.Field<string>("ConnectionString")?.ParseKeyValuePairs();

Expand Down Expand Up @@ -1521,7 +1522,7 @@ private static void PhasorDataSourceValidation(AdoDataConnection database, strin
}

// Make sure needed device statistic measurements exist, currently statistics are only associated with phasor devices so we filter based on protocol
foreach (DataRow device in database.Connection.RetrieveData(database.AdapterType, $"SELECT * FROM Device WHERE IsConcentrator = 0 AND NodeID = {nodeIDQueryString} AND ProtocolID IN ({protocolIDs})").Rows)
foreach (DataRow device in database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM Device WHERE IsConcentrator = 0 AND NodeID = {0} AND ProtocolID IN ({1})", nodeIDQueryString, protocolIDs).Rows)
{
foreach (DataRow statistic in deviceStatistics)
{
Expand All @@ -1543,7 +1544,7 @@ private static void PhasorDataSourceValidation(AdoDataConnection database, strin

// Make sure devices associated with a concentrator do not have any extraneous input stream statistic measurements - this can happen
// when a device was once a direct connect device but now is part of a concentrator...
foreach (DataRow inputStream in database.Connection.RetrieveData(database.AdapterType, $"SELECT * FROM Device WHERE (IsConcentrator = 0 AND ParentID IS NOT NULL) AND NodeID = {nodeIDQueryString} AND ProtocolID IN ({protocolIDs})").Rows)
foreach (DataRow inputStream in database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM Device WHERE (IsConcentrator = 0 AND ParentID IS NOT NULL) AND NodeID = {0} AND ProtocolID IN ({1})", nodeIDQueryString, protocolIDs).Rows)
{
firstStatisticExisted = false;

Expand Down Expand Up @@ -1588,14 +1589,14 @@ private static void PhasorDataSourceValidation(AdoDataConnection database, strin
statusMessage("Validating output stream measurements...");

// Make sure needed output stream statistic measurements exist
foreach (DataRow outputStream in database.Connection.RetrieveData(database.AdapterType, $"SELECT * FROM OutputStream WHERE NodeID = {nodeIDQueryString}").Rows)
foreach (DataRow outputStream in database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM OutputStream WHERE NodeID = {0}", nodeIDQueryString).Rows)
{
adapterID = outputStream.ConvertField<int>("ID");

// Load devices acronyms associated with this output stream
List<string> deviceAcronyms =
database.Connection.RetrieveData(database.AdapterType,
$"SELECT Acronym FROM OutputStreamDevice WHERE AdapterID = {adapterID} AND NodeID = {nodeIDQueryString}")
"SELECT Acronym FROM OutputStreamDevice WHERE AdapterID = {0} AND NodeID = {1}", adapterID, nodeIDQueryString)
.AsEnumerable()
.Select(row => row.Field<string>("Acronym"))
.ToList();
Expand All @@ -1607,7 +1608,7 @@ private static void PhasorDataSourceValidation(AdoDataConnection database, strin
deviceAcronyms.Sort(StringComparer.OrdinalIgnoreCase);

// Validate measurements associated with this output stream
foreach (DataRow outputStreamMeasurement in database.Connection.RetrieveData(database.AdapterType, $"SELECT * FROM OutputStreamMeasurement WHERE AdapterID = {adapterID} AND NodeID = {nodeIDQueryString}").Rows)
foreach (DataRow outputStreamMeasurement in database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM OutputStreamMeasurement WHERE AdapterID = {0} AND NodeID = {1}", adapterID, nodeIDQueryString).Rows)
{
// Parse output stream measurement signal reference
deviceSignalReference = new SignalReference(outputStreamMeasurement.Field<string>("SignalReference"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ public static IList<int> LoadKeys(AdoDataConnection database, string sortMember
if (!string.IsNullOrEmpty(sortMember))
sortClause = $"ORDER BY {sortMember} {sortDirection}";

DataTable calculatedMeasurementTable = database.Connection.RetrieveData(database.AdapterType, $"SELECT ID From CalculatedMeasurementDetail {sortClause}");
DataTable calculatedMeasurementTable = database.Connection.RetrieveData(database.AdapterType, "SELECT ID From CalculatedMeasurementDetail {0}", sortClause);

foreach (DataRow row in calculatedMeasurementTable.Rows)
{
Expand Down Expand Up @@ -531,12 +531,11 @@ public static ObservableCollection<CalculatedMeasurement> Load(AdoDataConnection
{
commaSeparatedKeys = keys.Select(key => key.ToString()).Aggregate((str1, str2) => $"{str1},{str2}");

query = string.Format("SELECT NodeID, ID, Acronym, Name, AssemblyName, " +
query = database.ParameterizedQueryString("SELECT NodeID, ID, Acronym, Name, AssemblyName, " +
"TypeName, ConnectionString, ConfigSection, InputMeasurements, OutputMeasurements, MinimumMeasurementsToUse, FramesPerSecond, LagTime, " +
"LeadTime, UseLocalClockAsRealTime, AllowSortsByArrival, LoadOrder, Enabled, IgnoreBadTimeStamps, TimeResolution, AllowPreemptivePublishing, " +
"DownSamplingMethod, NodeName, PerformTimeReasonabilityCheck From CalculatedMeasurementDetail WHERE ID IN ({0}) AND NodeID = '{1}'", commaSeparatedKeys, database.CurrentNodeID());

calculatedMeasurementTable = database.Connection.RetrieveData(database.AdapterType, query);
"DownSamplingMethod, NodeName, PerformTimeReasonabilityCheck From CalculatedMeasurementDetail WHERE ID IN ({0}) AND NodeID = '{1}'", "commaSeparatedKeys", "database.CurrentNodeID()");
calculatedMeasurementTable = database.Connection.RetrieveData(database.AdapterType, query, commaSeparatedKeys, database.CurrentNodeID());
calculatedMeasurementList = new CalculatedMeasurement[calculatedMeasurementTable.Rows.Count];

foreach (DataRow row in calculatedMeasurementTable.Rows)
Expand Down
Loading