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 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2316351
Removed Left over Access support and parametrized nodeID
clackner-gpa Nov 21, 2024
3801650
Parametrized MySQL Query
clackner-gpa Nov 21, 2024
53a3fb7
Parametrized query
clackner-gpa Nov 21, 2024
c190979
Parametrized queries
clackner-gpa Nov 21, 2024
2883858
DeviceAlarmStateAdapter
collins-self Dec 17, 2024
071a855
LocalOutPutAdapter
collins-self Dec 17, 2024
3be0a48
CommonPhasorServices
collins-self Dec 17, 2024
817e84e
Device.cs
collins-self Dec 17, 2024
ca6105e
OutputStream
collins-self Dec 17, 2024
c738474
CalculatedMeasurements
collins-self Dec 17, 2024
1c95bc8
OutputStreamDevice
collins-self Dec 17, 2024
6910976
DatabaseConfigurationLoader
collins-self Dec 17, 2024
ef8cb0b
Adapter and Alarm
collins-self Dec 18, 2024
4e1c3d7
measurement and historian
collins-self Dec 18, 2024
81df9e3
Node, Vendor, ModelController
collins-self Dec 18, 2024
f211313
string.format
collins-self Dec 18, 2024
d02a17d
PhasorServices and MetadataExportAdaper
collins-self Dec 23, 2024
d34c9d2
ExecuteScalar
collins-self Dec 23, 2024
3898d2a
ExecuteReader
collins-self Dec 23, 2024
687b41c
MetaDataImportAdapter, CommonPhasorServices
collins-self Dec 26, 2024
693d1c4
ErrorLogger
collins-self Dec 26, 2024
1a26f5f
TimeSeriesStartupOperations and Adapter
collins-self Dec 26, 2024
6c43e56
clean up fixes
collins-self Dec 27, 2024
878fbd7
OutputAdapter
collins-self Dec 30, 2024
aaf2ead
quick fixes to only commented reviews
collins-self Feb 1, 2025
520b5d6
refactored and undid changes to strings
collins-self Feb 5, 2025
2a7ca5c
updated query strings
collins-self Feb 5, 2025
2004c9d
fixed many string.format()'s and safe parameter passing
collins-self Feb 6, 2025
949feb9
whitespace
collins-self Feb 6, 2025
6906d71
removed bad changes with sql strings in parameters
collins-self Feb 7, 2025
ca4ad88
DatabaseConfigurationLoader
collins-self Feb 7, 2025
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 @@ -451,9 +451,10 @@ 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"));

// ReSharper disable once AccessToDisposedClosure
Expand Down
15 changes: 10 additions & 5 deletions Source/Libraries/Adapters/HistorianAdapters/LocalOutputAdapter.cs
Original file line number Diff line number Diff line change
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 @@ -1206,7 +1208,7 @@ private static void AddPIHistorianReaders(AdoDataConnection database, string nod
IEnumerable<DataRow> readers = database.Connection.RetrieveData(database.AdapterType, $"SELECT * FROM CustomInputAdapter WHERE NodeID = {nodeIDQueryString} AND TypeName = 'PIAdapters.PIPBInputAdapter'").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
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
Loading