Support multi-statement commands again and correctly handle parameters.

pull/48/head
Lau 6 years ago
parent 1781ce29df
commit 1066d34e5b

@ -30,7 +30,8 @@ namespace System.Data.Jet
#endif
_connection = source._connection;
_transaction = source._transaction;
InnerCommand = source.InnerCommand;
InnerCommand = (DbCommand) ((ICloneable) source.InnerCommand).Clone();
}
/// <summary>
@ -63,7 +64,7 @@ namespace System.Data.Jet
#endif
}
internal DbCommand InnerCommand { get; }
protected DbCommand InnerCommand { get; }
/// <summary>
/// Attempts to Cancels the command execution
@ -172,17 +173,31 @@ namespace System.Data.Jet
if (Connection.State != ConnectionState.Open)
throw new InvalidOperationException(Messages.CannotCallMethodInThisConnectionState("ExecuteReader", ConnectionState.Open, Connection.State));
ExpandParameters();
var commands = SplitCommands();
for (var i = 0; i < commands.Count - 1; i++)
{
commands[i]
.ExecuteNonQueryCore();
}
return commands[commands.Count - 1]
.ExecuteDbDataReaderCore(behavior);
}
protected virtual DbDataReader ExecuteDbDataReaderCore(CommandBehavior behavior)
{
InnerCommand.Connection = _connection.InnerConnection;
// OLE DB forces us to use an existing active transaction, if one is available.
InnerCommand.Transaction = _transaction?.WrappedTransaction ?? _connection.ActiveTransaction?.WrappedTransaction;
ExpandParameters();
LogHelper.ShowCommandText("ExecuteDbDataReader", InnerCommand);
if (JetStoreSchemaDefinitionRetrieve.TryGetDataReaderFromShowCommand(InnerCommand, _connection.JetFactory.InnerFactory, out var dataReader))
// Retrieve of store schema definition
// Retrieve from store schema definition.
return dataReader;
if (InnerCommand.CommandType != CommandType.Text)
@ -204,22 +219,6 @@ namespace System.Data.Jet
return dataReader;
}
private DbDataReader TryGetDataReaderForSelectRowCount(string commandText)
{
if (_selectRowCountRegularExpression.Match(commandText).Success)
{
if (_rowCount == null)
throw new InvalidOperationException("Invalid " + commandText + ". Run a DataReader before.");
var dataTable = new DataTable("Rowcount");
dataTable.Columns.Add("ROWCOUNT", typeof(int));
dataTable.Rows.Add(_rowCount.Value);
return new DataTableReader(dataTable);
}
return null;
}
/// <summary>
/// Executes the non query.
/// </summary>
@ -228,13 +227,23 @@ namespace System.Data.Jet
{
if (Connection == null)
throw new InvalidOperationException(Messages.PropertyNotInitialized(nameof(Connection)));
ExpandParameters();
return SplitCommands()
.Aggregate(0, (_, command) => command.ExecuteNonQueryCore());
}
protected virtual int ExecuteNonQueryCore()
{
if (Connection == null)
throw new InvalidOperationException(Messages.PropertyNotInitialized(nameof(Connection)));
LogHelper.ShowCommandText("ExecuteNonQuery", InnerCommand);
if (JetStoreDatabaseHandling.TryDatabaseOperation(this))
return 1;
if (JetRenameHandling.TryDatabaseOperation(Connection.ConnectionString, InnerCommand.CommandText))
return 1;
@ -248,13 +257,14 @@ namespace System.Data.Jet
if (InnerCommand.CommandType != CommandType.Text)
return InnerCommand.ExecuteNonQuery();
if (_selectRowCountRegularExpression.Match(InnerCommand.CommandText)
.Success)
{
// TODO: Fix exception message.
if (_rowCount == null)
throw new InvalidOperationException("Invalid " + InnerCommand.CommandText + ". Run a DataReader before.");
return _rowCount.Value;
}
@ -265,7 +275,7 @@ namespace System.Data.Jet
return 0;
InnerCommand.CommandText = newCommandText;
InlineTopParameters();
FixParameters();
@ -279,6 +289,28 @@ namespace System.Data.Jet
/// </summary>
/// <returns></returns>
public override object ExecuteScalar()
{
if (Connection == null)
throw new InvalidOperationException(Messages.PropertyNotInitialized(nameof(Connection)));
if (Connection.State != ConnectionState.Open)
throw new InvalidOperationException(Messages.CannotCallMethodInThisConnectionState(nameof(ExecuteScalar), ConnectionState.Open, Connection.State));
ExpandParameters();
var commands = SplitCommands();
for (var i = 0; i < commands.Count - 1; i++)
{
commands[i]
.ExecuteNonQueryCore();
}
return commands[commands.Count - 1]
.ExecuteScalarCore();
}
protected virtual object ExecuteScalarCore()
{
if (Connection == null)
throw new InvalidOperationException(Messages.PropertyNotInitialized(nameof(Connection)));
@ -291,13 +323,11 @@ namespace System.Data.Jet
// OLE DB forces us to use an existing active transaction, if one is available.
InnerCommand.Transaction = _transaction?.WrappedTransaction ?? _connection.ActiveTransaction?.WrappedTransaction;
ExpandParameters();
LogHelper.ShowCommandText("ExecuteScalar", InnerCommand);
if (JetStoreSchemaDefinitionRetrieve.TryGetDataReaderFromShowCommand(InnerCommand, _connection.JetFactory.InnerFactory, out var dataReader))
{
// Retrieve of store schema definition
// Retrieve from store schema definition.
if (dataReader.HasRows)
{
dataReader.Read();
@ -313,6 +343,76 @@ namespace System.Data.Jet
return InnerCommand.ExecuteScalar();
}
protected virtual IReadOnlyList<JetCommand> SplitCommands()
{
// At this point, all parameters have already been expanded.
var parser = new JetCommandParser(CommandText);
var commandDelimiters = parser.GetStateIndices(';');
var currentCommandStart = 0;
var usedParameterCount = 0;
var commands = new List<JetCommand>();
if (commandDelimiters.Count > 0)
{
foreach (var commandDelimiter in commandDelimiters)
{
var commandText = CommandText.Substring(currentCommandStart, commandDelimiter - currentCommandStart)
.Trim();
if (!string.IsNullOrEmpty(commandText))
{
var command = (JetCommand) ((ICloneable) this).Clone();
command.CommandText = commandText;
for (var i = 0; i < usedParameterCount; i++)
{
command.Parameters.RemoveAt(0);
}
var parameterIndices = parser.GetStateIndices(
new[] {'@', '?'},
currentCommandStart,
commandDelimiter - currentCommandStart);
while (command.Parameters.Count > parameterIndices.Count)
{
command.Parameters.RemoveAt(parameterIndices.Count);
}
usedParameterCount += parameterIndices.Count;
commands.Add(command);
}
currentCommandStart = commandDelimiter + 1;
}
}
else
{
commands.Add(this);
}
return commands.AsReadOnly();
}
private DbDataReader TryGetDataReaderForSelectRowCount(string commandText)
{
if (_selectRowCountRegularExpression.Match(commandText)
.Success)
{
if (_rowCount == null)
throw new InvalidOperationException("Invalid " + commandText + ". Run a DataReader before.");
var dataTable = new DataTable("Rowcount");
dataTable.Columns.Add("ROWCOUNT", typeof(int));
dataTable.Rows.Add(_rowCount.Value);
return new DataTableReader(dataTable);
}
return null;
}
private bool CheckExists(string commandText, out string newCommandText)
{
var match = _ifStatementRegex.Match(commandText);
@ -345,10 +445,10 @@ namespace System.Data.Jet
private void FixParameters()
{
var parameters = InnerCommand.Parameters;
if (parameters.Count == 0)
return;
foreach (DbParameter parameter in parameters)
{
if (parameter.Value is TimeSpan ts)
@ -403,7 +503,8 @@ namespace System.Data.Jet
{
// We inline all TOP clause parameters of all SELECT statements, because Jet does not support parameters
// in TOP clauses.
var parameters = InnerCommand.Parameters.Cast<DbParameter>().ToList();
var parameters = InnerCommand.Parameters.Cast<DbParameter>()
.ToList();
if (parameters.Count > 0)
{
@ -412,7 +513,10 @@ namespace System.Data.Jet
while ((commandText = _topParameterRegularExpression.Replace(
lastCommandText,
match => Convert.ToInt32(ExtractParameter(commandText, match.Value, match.Index, parameters).Value).ToString(),
match => Convert.ToInt32(
ExtractParameter(commandText, match.Index, parameters)
.Value)
.ToString(),
1)) != lastCommandText)
{
lastCommandText = commandText;
@ -429,13 +533,13 @@ namespace System.Data.Jet
=> fragment.StartsWith("@") ||
fragment.Equals("?");
protected virtual DbParameter ExtractParameter(string commandText, string name, int count, List<DbParameter> parameters)
protected virtual DbParameter ExtractParameter(string commandText, int count, List<DbParameter> parameters)
{
var indices = GetParameterIndices(commandText.Substring(0, count));
var parameter = InnerCommand.Parameters[indices.Count];
parameters.RemoveAt(indices.Count);
return parameter;
}
@ -445,7 +549,7 @@ namespace System.Data.Jet
{
return;
}
var indices = GetParameterIndices(InnerCommand.CommandText);
if (indices.Count <= 0)
@ -454,7 +558,7 @@ namespace System.Data.Jet
}
var placeholders = GetParameterPlaceholders(InnerCommand.CommandText, indices);
if (placeholders.All(t => t.Name.StartsWith("@")))
{
MatchParametersAndPlaceholders(placeholders);
@ -468,9 +572,11 @@ namespace System.Data.Jet
.Insert(placeholder.Index, "?");
}
}
InnerCommand.Parameters.Clear();
InnerCommand.Parameters.AddRange(placeholders.Select(p => p.Parameter).ToArray());
InnerCommand.Parameters.AddRange(
placeholders.Select(p => p.Parameter)
.ToArray());
}
else if (placeholders.All(t => t.Name == "?"))
{
@ -524,7 +630,7 @@ namespace System.Data.Jet
protected virtual IReadOnlyList<ParameterPlaceholder> GetParameterPlaceholders(string commandText, IEnumerable<int> indices)
{
var placeholders = new List<ParameterPlaceholder>();
foreach (var index in indices)
{
var match = Regex.Match(commandText.Substring(index), @"^(?:\?|@\w+)");
@ -533,8 +639,8 @@ namespace System.Data.Jet
{
throw new InvalidOperationException("Invalid parameter placeholder found.");
}
placeholders.Add(new ParameterPlaceholder{ Index = index, Name = match.Value });
placeholders.Add(new ParameterPlaceholder {Index = index, Name = match.Value});
}
return placeholders.AsReadOnly();
@ -581,7 +687,7 @@ namespace System.Data.Jet
/// <returns>The created object</returns>
object ICloneable.Clone()
=> new JetCommand(this);
protected class ParameterPlaceholder
{
public int Index { get; set; }

Loading…
Cancel
Save