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 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
25 changes: 15 additions & 10 deletions Source/Libraries/Adapters/HistorianAdapters/LocalOutputAdapter.cs
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 @@ -1119,12 +1119,14 @@ private static void OptimizeLocalHistorianSettings(AdoDataConnection database, s
string archiveLocation = FilePath.GetDirectoryName(settings["FileName"].Value);
string adapterName = $"{instanceName}READER";
string connectionString = string.Format("archiveLocation={0}; instanceName={1}; sourceIDs={1}; publicationInterval=333333; connectOnDemand=true", archiveLocation, instanceName);
string query = "INSERT INTO CustomInputAdapter(NodeID, AdapterName, AssemblyName, TypeName, ConnectionString, LoadOrder, Enabled) " + $"VALUES({nodeIDQueryString}, @adapterName, 'HistorianAdapters.dll', 'HistorianAdapters.LocalInputAdapter', @connectionString, 0, 1)";

string query = database.ParameterizedQueryString("INSERT INTO " +
"CustomInputAdapter(NodeID, AdapterName, AssemblyName, TypeName, ConnectionString, LoadOrder, Enabled) " +
"VALUES({0}, @{1}, 'HistorianAdapters.dll', 'HistorianAdapters.LocalInputAdapter', @{2}, 0, 1)",
"nodeIDQueryString", "adapterName", "connectionString");
if (database.IsOracle)
query = query.Replace('@', ':');

database.Connection.ExecuteNonQuery(query, adapterName, connectionString);
database.Connection.ExecuteNonQuery(query, nodeIDQueryString, adapterName, connectionString);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -1202,11 +1204,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 Expand Up @@ -1252,7 +1254,10 @@ private static void AddPIHistorianReaders(AdoDataConnection database, string nod

string connectionString = string.IsNullOrEmpty(userName) ? $"ServerName={serverName}; ConnectTimeout={connectTimeout}; sourceIDs={instanceName}; connectOnDemand=true" : $"ServerName={serverName}; UserName={userName}; Password={password.ToNonNullString()}; ConnectTimeout={connectTimeout}; sourceIDs={instanceName}; connectOnDemand=true";

string query = "INSERT INTO CustomInputAdapter(NodeID, AdapterName, AssemblyName, TypeName, ConnectionString, LoadOrder, Enabled) " + $"VALUES({nodeIDQueryString}, @adapterName, 'PIAdapters.dll', 'PIAdapters.PIPBInputAdapter', @connectionString, 0, 1)";
string query = database.ParameterizedQueryString("INSERT INTO " +
"CustomInputAdapter(NodeID, AdapterName, AssemblyName, TypeName, ConnectionString, LoadOrder, Enabled) " +
"VALUES({0}, @{1}, 'PIAdapters.dll', 'PIAdapters.PIPBInputAdapter', @{2}, 0, 1)",
"nodeIDQueryString", "adapterName", "connectionString");

if (database.IsOracle)
query = query.Replace('@', ':');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ private DataSet AcquireMetadata()
DataSet metadata = new DataSet();

// Initialize active node ID
Guid nodeID = Guid.Parse(dbConnection.ExecuteScalar($"SELECT NodeID FROM IaonActionAdapter WHERE ID = {ID}").ToString());
Guid nodeID = Guid.Parse(dbConnection.ExecuteScalar("SELECT NodeID FROM IaonActionAdapter WHERE ID = {0}", ID).ToString());

// Copy key metadata tables
foreach (string tableExpression in MetadataTables.Split(';'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ private Action<DataSet> GetSynchronizeMetadataAction()

// Determine the active node ID - we cache this since this value won't change for the lifetime of this class
if (nodeID == Guid.Empty)
nodeID = Guid.Parse(command.ExecuteScalar($"SELECT NodeID FROM IaonActionAdapter WHERE ID = {(int)ID}", MetadataSynchronizationTimeout).ToString());
nodeID = Guid.Parse(command.ExecuteScalar("SELECT NodeID FROM IaonActionAdapter WHERE ID = {0}", (int)ID, MetadataSynchronizationTimeout).ToString());

// Determine the protocol record auto-inc ID value for the gateway transport protocol (GEP) - this value is also cached since it shouldn't change for the lifetime of this class
if (virtualProtocolID == 0)
Expand All @@ -462,19 +462,19 @@ private Action<DataSet> GetSynchronizeMetadataAction()
if (sourceID == null || sourceID == DBNull.Value)
{
// Get a historian ID, but exclude the STAT historian
object randomHistorianID = command.ExecuteScalar($"SELECT ID FROM Historian WHERE Acronym <> 'STAT'", MetadataSynchronizationTimeout);
object randomHistorianID = command.ExecuteScalar("SELECT ID FROM Historian WHERE Acronym <> 'STAT'", MetadataSynchronizationTimeout);
command.ExecuteNonQuery(insertParentDeviceSql, MetadataSynchronizationTimeout, database.Guid(nodeID), randomHistorianID, ParentDeviceAcronym, ParentDeviceAcronym, virtualProtocolID);
sourceID = command.ExecuteScalar(parentDeviceIDSql, MetadataSynchronizationTimeout, ParentDeviceAcronym);
}

int parentID = Convert.ToInt32(sourceID);

// Validate that the subscriber device is marked as a concentrator (we are about to associate children devices with it)
if (!command.ExecuteScalar($"SELECT IsConcentrator FROM Device WHERE ID = {parentID}", MetadataSynchronizationTimeout).ToString().ParseBoolean())
command.ExecuteNonQuery($"UPDATE Device SET IsConcentrator = 1 WHERE ID = {parentID}", MetadataSynchronizationTimeout);
if (!command.ExecuteScalar("SELECT IsConcentrator FROM Device WHERE ID = {0}", parentID, MetadataSynchronizationTimeout).ToString().ParseBoolean())
command.ExecuteNonQuery("UPDATE Device SET IsConcentrator = 1 WHERE ID = {0}", parentID, MetadataSynchronizationTimeout);

// Get any historian associated with the subscriber device
object historianID = command.ExecuteScalar($"SELECT HistorianID FROM Device WHERE ID = {parentID}", MetadataSynchronizationTimeout);
object historianID = command.ExecuteScalar("SELECT HistorianID FROM Device WHERE ID = {0}", parentID, MetadataSynchronizationTimeout);

// Ascertain total number of actions required for all metadata synchronization so some level feed back can be provided on progress
initSyncProgress(metadata.Tables.Cast<DataTable>().Sum(dataTable => (long)dataTable.Rows.Count) + 3);
Expand Down Expand Up @@ -911,7 +911,7 @@ private Action<DataSet> GetSynchronizeMetadataAction()
List<int> sourceIndicies;

if (definedSourceIndicies.TryGetValue(id, out sourceIndicies))
command.ExecuteNonQuery(deletePhasorSql + $" AND SourceIndex NOT IN ({string.Join(",", sourceIndicies)})", MetadataSynchronizationTimeout, id);
command.ExecuteNonQuery(deletePhasorSql + " AND SourceIndex NOT IN ({0})", string.Join(",", sourceIndicies), MetadataSynchronizationTimeout, id);
else
command.ExecuteNonQuery(deletePhasorSql, MetadataSynchronizationTimeout, id);
}
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
2 changes: 1 addition & 1 deletion Source/Libraries/Adapters/PIAdapters/PIOutputAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2475,7 +2475,7 @@ protected override void ExecuteMetadataRefresh()
{
// Attempt to look up last update time for record
database ??= new AdoDataConnection("systemSettings");
updateTime = Convert.ToDateTime(database.Connection.ExecuteScalar($"SELECT UpdatedOn FROM Measurement WHERE SignalID = '{signalID}'"));
updateTime = Convert.ToDateTime(database.Connection.ExecuteScalar("SELECT UpdatedOn FROM Measurement WHERE SignalID = '{0}'", signalID));
}
}
catch
Expand Down
Loading