2 // System.Data.ProviderBase.AbstractDbCommand
\r
5 // Konstantin Triger <kostat@mainsoft.com>
6 // Boris Kirzner <borisk@mainsoft.com>
8 // (C) 2005 Mainsoft Corporation (http://www.mainsoft.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Text.RegularExpressions;
\r
36 using System.Collections;
\r
38 using System.Data.Common;
\r
43 #if !USE_DOTNET_REGEXP
\r
44 using java.util.regex;
\r
47 namespace System.Data.ProviderBase
\r
49 public abstract class AbstractDbCommand : DbCommand, ICloneable
\r
51 #region ProcedureColumnCache
53 internal sealed class ProcedureColumnCache : AbstractDbMetaDataCache
\r
55 internal ArrayList GetProcedureColumns(AbstractDBConnection connection, String commandText,AbstractDbCommand command)
\r
57 string connectionCatalog = connection.JdbcConnection.getCatalog();
\r
58 string key = String.Concat(connection.ConnectionString, connectionCatalog, commandText);
\r
59 System.Collections.Hashtable cache = Cache;
\r
61 ArrayList col = cache[key] as ArrayList;
\r
67 col = connection.GetProcedureColumns(commandText,command);
\r
76 #region SqlStatementsHelper
78 internal sealed class SqlStatementsHelper
81 #if USE_DOTNET_REGEXP
\r
82 internal static readonly Regex NamedParameterStoredProcedureRegExp = new Regex(@"^\s*{?\s*((?<RETVAL>@\w+)\s*=\s*)?call\s+(?<PROCNAME>(((\[[^\]]*\])|([^\.\(])*)\s*\.\s*){0,2}(\[[^\]]*\]|((\s*[^\.\(\)\{\}\s])+)))\s*(\(\s*(?<USERPARAM>((""([^""]|(""""))*"")|('([^']|(''))*')|[^,])*)?\s*(,\s*(?<USERPARAM>((""([^""]|(""""))*"")|('([^']|(''))*')|[^,])*)\s*)*\))?\s*}?\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
\r
83 internal static readonly Regex SimpleParameterStoredProcedureRegExp = new Regex(@"^\s*{?\s*((?<RETVAL>\?)\s*=\s*)?call\s+(?<PROCNAME>(((\[[^\]]*\])|([^\.\(])*)\s*\.\s*){0,2}(\[[^\]]*\]|((\s*[^\.\(\)\{\}\s])+)))\s*(\(\s*(?<USERPARAM>((""([^""]|(""""))*"")|('([^']|(''))*')|[^,])*)?\s*(,\s*(?<USERPARAM>((""([^""]|(""""))*"")|('([^']|(''))*')|[^,])*)\s*)*\))?\s*}?\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
\r
84 internal static readonly Regex ForBrowseStatementReqExp = new Regex(@"\s+FOR\s+BROWSE\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
\r
86 internal static readonly Pattern NamedParameterStoredProcedureRegExp = Pattern.compile(@"^\s*\{?\s*(?:(@\w+)\s*=\s*)?call\s+((?:(?:(?:\[[^\]]*\])|(?:[^\.\(\)\{\}\[\]])*)\s*\.\s*){0,2}(?:\[[^\]]*\]|(?:(?:\s*[^\.\(\)\{\}\[\]])+)))\s*(?:\((.*)\))?\s*\}?\s*$", Pattern.CASE_INSENSITIVE);
\r
87 internal static readonly Pattern SimpleParameterStoredProcedureRegExp = Pattern.compile(@"^\s*\{?\s*(?:(\?)\s*=\s*)?call\s+((?:(?:(?:\[[^\]]*\])|(?:[^\.\(\)\{\}\[\]])*)\s*\.\s*){0,2}(?:\[[^\]]*\]|(?:(?:\s*[^\.\(\)\{\}\[\]])+)))\s*(?:\((.*)\))?\s*\}?\s*$", Pattern.CASE_INSENSITIVE);
\r
88 internal static readonly Pattern ForBrowseStatementReqExp = Pattern.compile(@"\s+FOR\s+BROWSE\s*$", Pattern.CASE_INSENSITIVE);
\r
91 internal static readonly SimpleRegex NamedParameterRegExp = new SqlParamsRegex();
\r
92 internal static readonly SimpleRegex SimpleParameterRegExp = new OleDbParamsRegex();
\r
94 internal static readonly SimpleRegex CompoundStatementSplitterReqExp = new CharacterSplitterRegex(';');
\r
95 internal static readonly SimpleRegex ProcedureParameterSplitterReqExp = new CharacterSplitterRegex(',');
\r
100 #endregion // SqlStatementsHelper
106 CommandType _commandType;
107 bool _designTimeVisible;
108 UpdateRowSource _updatedRowSource;
\r
110 private DbParameterCollection _parameters;
\r
111 private java.sql.Statement _statement;
\r
112 private AbstractDBConnection _connection;
\r
113 private AbstractTransaction _transaction;
\r
114 private bool _isCommandPrepared;
\r
115 private CommandBehavior _behavior;
\r
116 private ArrayList _internalParameters;
\r
117 string _javaCommandText;
\r
118 private int _recordsAffected;
\r
119 private ResultSet _currentResultSet;
\r
120 private DbDataReader _currentReader;
\r
121 private bool _nullParametersInPrepare;
\r
122 private bool _hasResultSet;
\r
123 private bool _explicitPrepare;
\r
125 static ProcedureColumnCache _procedureColumnCache = new ProcedureColumnCache();
\r
127 #endregion // Fields
\r
129 #region Constructors
\r
131 public AbstractDbCommand(
\r
133 AbstractDBConnection connection,
\r
134 AbstractTransaction transaction)
\r
136 _connection = connection;
\r
137 _commandText = cmdText;
\r
138 _transaction = transaction;
\r
140 _commandTimeout = 30;
141 _commandType = CommandType.Text;
142 _designTimeVisible = true;
143 _updatedRowSource = UpdateRowSource.Both;
\r
145 _isCommandPrepared = false;
\r
146 _explicitPrepare = false;
\r
147 _recordsAffected = -1;
\r
148 if (connection != null) {
\r
149 connection.AddReference(this);
\r
153 #endregion // Constructors
\r
157 public override int CommandTimeout {
158 get { return _commandTimeout; }
159 set { _commandTimeout = value; }
162 public override CommandType CommandType {
163 get { return _commandType; }
164 set { _commandType = value; }
167 public override bool DesignTimeVisible {
168 get { return _designTimeVisible; }
169 set { _designTimeVisible = value; }
172 public override UpdateRowSource UpdatedRowSource {
173 get { return _updatedRowSource; }
174 set { _updatedRowSource = value; }
177 protected override DbParameterCollection DbParameterCollection
\r
180 if (_parameters == null) {
\r
181 _parameters = CreateParameterCollection(this);
\r
183 return _parameters;
\r
187 protected override DbConnection DbConnection
\r
189 get { return (DbConnection)_connection; }
\r
191 if (value == _connection) {
\r
195 if (_currentReader != null && !_currentReader.IsClosed) {
\r
196 throw ExceptionHelper.ConnectionIsBusy(this.GetType().Name,((AbstractDBConnection)_connection).InternalState);
\r
198 if (_connection != null) {
\r
199 _connection.RemoveReference(this);
\r
201 _connection = (AbstractDBConnection) value;
\r
202 if (_connection != null) {
\r
203 _connection.AddReference(this);
\r
208 protected override DbTransaction DbTransaction
\r
210 get { return _transaction != null ? _transaction.ActiveTransaction : null; }
\r
211 set { _transaction = (AbstractTransaction)value; }
\r
214 public override string CommandText
\r
216 get { return _commandText; }
\r
218 if (CommandText == null || String.Compare(CommandText, value, true) != 0) {
\r
219 _commandText = value;
\r
220 _isCommandPrepared = false;
\r
221 _explicitPrepare = false;
\r
226 internal CommandBehavior Behavior
\r
228 get { return _behavior; }
\r
229 set { _behavior = value; }
\r
232 bool IsCommandPrepared
\r
234 get { return _isCommandPrepared; }
\r
235 set { _isCommandPrepared = value; }
\r
238 bool NullParametersInPrepare
\r
240 get { return _nullParametersInPrepare; }
\r
241 set { _nullParametersInPrepare = value; }
\r
244 protected ArrayList InternalParameters
\r
247 if (_internalParameters == null) {
\r
248 _internalParameters = new ArrayList();
\r
250 return _internalParameters;
\r
254 // Number of records affected by execution of batch statement
\r
255 // -1 for SELECT statements.
\r
256 internal int RecordsAffected
\r
259 return _recordsAffected;
\r
263 // AbstractDbCommand acts as IEnumerator over JDBC statement
\r
264 // AbstractDbCommand.CurrentResultSet corresponds to IEnumerator.Current
\r
265 protected internal virtual ResultSet CurrentResultSet
\r
269 if (_currentResultSet == null && _hasResultSet) {
\r
270 _currentResultSet = _statement.getResultSet();
\r
272 return _currentResultSet;
\r
274 catch(SQLException e) {
\r
275 throw new Exception(e.Message, e);
\r
280 protected internal java.sql.Statement Statement
\r
282 get { return _statement; }
\r
284 #if USE_DOTNET_REGEX
\r
285 protected virtual Regex StoredProcedureRegExp
\r
287 protected virtual Pattern StoredProcedureRegExp
\r
290 get { return SqlStatementsHelper.SimpleParameterStoredProcedureRegExp; }
\r
293 protected virtual SimpleRegex ParameterRegExp
\r
295 get { return SqlStatementsHelper.SimpleParameterRegExp; }
\r
298 #endregion // Properties
\r
302 protected abstract DbParameter CreateParameterInternal();
\r
304 protected abstract void CheckParameters();
\r
306 protected abstract DbDataReader CreateReader();
\r
308 protected abstract DbParameterCollection CreateParameterCollection(AbstractDbCommand parent);
\r
310 protected internal abstract SystemException CreateException(SQLException e);
\r
312 public override int ExecuteNonQuery ()
314 IDataReader reader = null;
316 reader = ExecuteReader ();
322 return reader.RecordsAffected;
325 public override object ExecuteScalar ()
327 IDataReader reader = ExecuteReader(CommandBehavior.SingleRow | CommandBehavior.SequentialAccess);
331 if (reader.FieldCount > 0 && reader.Read ())
332 return reader.GetValue (0);
334 while (reader.NextResult ());
341 public virtual void ResetCommandTimeout ()
343 _commandTimeout = 30;
346 public override void Cancel()
\r
349 if (_statement != null)
\r
350 _statement.cancel();
\r
353 // MSDN says : "If there is nothing to cancel, nothing happens.
\r
354 // However, if there is a command in process, and the attempt to cancel fails,
\r
355 // no exception is generated."
\r
359 protected virtual bool SkipParameter(DbParameter parameter)
\r
364 protected sealed override DbParameter CreateDbParameter()
\r
366 return CreateParameterInternal();
\r
369 internal ArrayList DeriveParameters(string procedureName, bool throwIfNotExist)
\r
372 ArrayList col = _procedureColumnCache.GetProcedureColumns((AbstractDBConnection)Connection, procedureName, this);
\r
374 if (throwIfNotExist)
\r
375 throw ExceptionHelper.NoStoredProcedureExists(procedureName);
\r
376 col = new ArrayList();
\r
381 catch(SQLException e) {
\r
382 throw CreateException(e);
\r
386 string CreateTableDirectCommandText(string tableNames) {
\r
387 string forBrowse = String.Empty;
\r
388 if ((Behavior & CommandBehavior.KeyInfo) != 0) {
\r
389 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
390 if (connection != null) {
\r
391 string dbname = connection.JdbcConnection.getMetaData().getDatabaseProductName();
\r
392 if (dbname == "Microsoft SQL Server") //must add "FOR BROWSE" for selects
\r
393 forBrowse = " FOR BROWSE";
\r
397 string[] names = tableNames.Split(',');
\r
398 StringBuilder sb = new StringBuilder();
\r
400 for(int i = 0; i < names.Length; i++) {
\r
401 sb.Append("SELECT * FROM ");
\r
402 sb.Append(names[i]);
\r
403 sb.Append(forBrowse);
\r
407 if(names.Length <= 1) {
\r
408 sb.Remove(sb.Length - 1,1);
\r
410 return sb.ToString();
\r
413 private string PrepareCommandTextAndParameters()
\r
415 NullParametersInPrepare = false;
\r
416 switch (CommandType) {
\r
417 case CommandType.TableDirect :
\r
418 return CreateTableDirectCommandText(CommandText);
\r
419 case CommandType.StoredProcedure :
\r
420 return CreateStoredProcedureCommandTextSimple(CommandText, Parameters, DeriveParameters(CommandText, false));
\r
421 case CommandType.Text :
\r
423 int userParametersPosition = 0;
\r
424 int charsConsumed = 0;
\r
425 StringBuilder sb = new StringBuilder(CommandText.Length);
\r
427 for (SimpleMatch match = SqlStatementsHelper.CompoundStatementSplitterReqExp.Match(CommandText);
\r
429 match = match.NextMatch()) {
\r
431 int length = match.Length;
\r
436 int start = match.Index;
\r
437 string value = match.Value;
\r
439 sb.Append(CommandText, charsConsumed, start-charsConsumed);
\r
440 charsConsumed = start + length;
\r
442 #if USE_DOTNET_REGEX
\r
443 Match storedProcMatch = StoredProcedureRegExp.Match(value);
\r
444 // count parameters for all kinds of simple statements
\r
445 userParametersPosition +=
\r
446 (storedProcMatch.Success) ?
\r
447 // statement is stored procedure call
\r
448 CreateStoredProcedureCommandText(sb, value, storedProcMatch, Parameters, userParametersPosition) :
\r
449 // statement is a simple SQL query
\r
450 PrepareSimpleQuery(sb, value, Parameters, userParametersPosition);
\r
452 Matcher storedProcMatch = StoredProcedureRegExp.matcher((java.lang.CharSequence)(object)value);
\r
453 userParametersPosition +=
\r
454 (storedProcMatch.find()) ?
\r
455 // statement is stored procedure call
\r
456 CreateStoredProcedureCommandText(sb, value, storedProcMatch, Parameters, userParametersPosition) :
\r
457 // statement is a simple SQL query
\r
458 PrepareSimpleQuery(sb, value, Parameters, userParametersPosition);
\r
462 sb.Append(CommandText, charsConsumed, CommandText.Length-charsConsumed);
\r
464 return sb.ToString();
\r
469 string CreateStoredProcedureCommandTextSimple(string procedureName, IDataParameterCollection userParams, IList derivedParams) {
\r
470 StringBuilder sb = new StringBuilder();
\r
472 int curUserPos = 0;
\r
473 int curDerivedPos = 0;
\r
474 bool addParas = true;
\r
475 string trimedProcedureName = (procedureName != null) ? procedureName.TrimEnd() : String.Empty;
\r
476 if (trimedProcedureName.Length > 0 && trimedProcedureName[trimedProcedureName.Length-1] == ')')
\r
479 AbstractDbParameter derivedParam = (derivedParams.Count > 0) ? (AbstractDbParameter)derivedParams[curDerivedPos] : null;
\r
480 if (derivedParam != null) {
\r
481 if (derivedParam.Direction == ParameterDirection.ReturnValue)
\r
484 derivedParam = null; //play as if there is no retval parameter
\r
486 AbstractDbParameter returnValueParameter = GetReturnParameter (userParams);
\r
487 if (returnValueParameter != null) {
\r
489 InternalParameters.Add(returnValueParameter);
\r
490 sb.Append("{? = call ");
\r
492 if (derivedParam != null && !returnValueParameter.IsDbTypeSet) {
\r
493 returnValueParameter.JdbcType = derivedParam.JdbcType;
\r
497 sb.Append("{call ");
\r
500 sb.Append(procedureName);
\r
504 bool needComma = false;
\r
505 for (int i = curDerivedPos; i < derivedParams.Count; i++) {
\r
506 AbstractDbParameter derivedParameter = (AbstractDbParameter)derivedParams[curDerivedPos++];
\r
508 bool addParam = false;
\r
510 if (derivedParameter.IsSpecial) {
\r
511 // derived parameter is special - never appears in user parameters or user values
\r
512 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
516 AbstractDbParameter userParameter = GetUserParameter(derivedParameter.Placeholder, userParams, curUserPos);
\r
517 if (userParameter != null) {
\r
519 InternalParameters.Add(userParameter);
\r
522 if (derivedParameter != null && !userParameter.IsDbTypeSet) {
\r
523 userParameter.JdbcType = derivedParameter.JdbcType;
\r
538 for (int i = curUserPos; i < userParams.Count; i++) {
\r
544 AbstractDbParameter userParameter = (AbstractDbParameter)userParams[curUserPos++];
\r
545 InternalParameters.Add(userParameter);
\r
553 return sb.ToString();
\r
557 /// We suppose that user parameters are in the same order as devived parameters except the special cases
\r
558 /// (return value, oracle ref cursors etc.)
\r
560 //protected virtual string CreateStoredProcedureCommandText(string procedureName, IList userParametersList, int userParametersListStart/*, int userParametersListCount*/, string[] userValuesList, ArrayList derivedParametersList)
\r
561 #if USE_DOTNET_REGEX
\r
562 int CreateStoredProcedureCommandText(StringBuilder sb, string sql, Match match, IDataParameterCollection userParams, int userParamsStartPosition)
\r
564 int CreateStoredProcedureCommandText(StringBuilder sb, string sql, Matcher match, IDataParameterCollection userParams, int userParamsStartPosition)
\r
567 int curUserPos = userParamsStartPosition;
\r
568 #if USE_DOTNET_REGEX
\r
569 Group procNameGroup = null;
\r
571 for (Match procNameMatch = match; procNameMatch.Success; procNameMatch = procNameMatch.NextMatch()){
\r
572 procNameGroup = match.Groups["PROCNAME"];
\r
573 if (!procNameGroup.Success) {
\r
578 if (procNameGroup == null || !procNameGroup.Success)
\r
579 throw new ArgumentException("Not a stored procedure call: '{0}'", sql);
\r
581 ArrayList derivedParameters = DeriveParameters(procNameGroup.Value, false);
\r
583 ArrayList derivedParameters = DeriveParameters(match.group(2).Trim(), false);
\r
585 int curDerivedPos = 0;
\r
587 AbstractDbParameter retValderivedParameter = curDerivedPos < derivedParameters.Count ?
\r
588 (AbstractDbParameter)derivedParameters[curDerivedPos] : null;
\r
589 if (retValderivedParameter != null && retValderivedParameter.Direction == ParameterDirection.ReturnValue)
\r
592 int queryCurrentPosition = 0;
\r
594 #if USE_DOTNET_REGEX
\r
595 for (Match retValMatch = match; retValMatch.Success; retValMatch = retValMatch.NextMatch()){
\r
596 Group retval = retValMatch.Groups["RETVAL"];
\r
597 if (!retval.Success) {
\r
601 int retvalIndex = retval.Index;
\r
602 string retvalValue = retval.Value;
\r
603 int retvalLength = retval.Length;
\r
605 int retvalIndex = match.start(1);
\r
606 for (;retvalIndex >= 0;) {
\r
607 string retvalValue = match.group(1);
\r
608 int retvalLength = retvalValue.Length;
\r
611 sb.Append(sql, queryCurrentPosition, retvalIndex);
\r
612 AbstractDbParameter userParameter = GetUserParameter(retvalValue, userParams, curUserPos);
\r
613 if (userParameter != null) {
\r
615 InternalParameters.Add(userParameter);
\r
617 if (retValderivedParameter != null && !userParameter.IsDbTypeSet) {
\r
618 userParameter.JdbcType = retValderivedParameter.JdbcType;
\r
624 sb.Append(retvalValue);
\r
627 queryCurrentPosition = (retvalIndex + retvalLength);
\r
632 #if USE_DOTNET_REGEX
\r
633 sb.Append(sql, queryCurrentPosition, procNameGroup.Index + procNameGroup.Length - queryCurrentPosition);
\r
634 queryCurrentPosition = procNameGroup.Index + procNameGroup.Length;
\r
636 sb.Append(sql, queryCurrentPosition, match.end(2) - queryCurrentPosition);
\r
637 queryCurrentPosition = match.end(2);
\r
640 bool hasUserParams = false;
\r
642 #if USE_DOTNET_REGEX
\r
643 must rewrite the regex to not parse params to have single code with java regex
\r
645 int paramsStart = match.start(3);
\r
646 if (paramsStart >= 0) {
\r
649 hasUserParams = true;
\r
650 sb.Append(sql,queryCurrentPosition,paramsStart - queryCurrentPosition);
\r
651 queryCurrentPosition = paramsStart;
\r
653 for (SimpleMatch m = SqlStatementsHelper.ProcedureParameterSplitterReqExp.Match(match.group(3));
\r
654 m.Success;m = m.NextMatch()) {
\r
656 SimpleCapture parameterCapture = m;
\r
657 sb.Append(sql,queryCurrentPosition,paramsStart + parameterCapture.Index - queryCurrentPosition);
\r
659 // advance in query
\r
660 queryCurrentPosition = paramsStart + parameterCapture.Index + parameterCapture.Length;
\r
662 AbstractDbParameter derivedParameter = curDerivedPos < derivedParameters.Count ?
\r
663 (AbstractDbParameter)derivedParameters[curDerivedPos++] : null;
\r
665 //check for special params
\r
666 while (derivedParameter != null && derivedParameter.IsSpecial) {
\r
667 // derived parameter is special - never appears in user parameters or user values
\r
668 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
672 derivedParameter = curDerivedPos < derivedParameters.Count ?
\r
673 (AbstractDbParameter)derivedParameters[curDerivedPos++] : null;
\r
676 AbstractDbParameter userParameter = GetUserParameter(parameterCapture.Value.Trim(), userParams, curUserPos);
\r
678 if (userParameter != null) {
\r
680 InternalParameters.Add(userParameter);
\r
681 if (derivedParameter != null && !userParameter.IsDbTypeSet) {
\r
682 userParameter.JdbcType = derivedParameter.JdbcType;
\r
684 // advance in user parameters
\r
688 sb.Append(parameterCapture.Value);
\r
693 bool addedSpecialParams = false;
\r
695 for (int i = curDerivedPos; i < derivedParameters.Count;) {
\r
696 AbstractDbParameter derivedParameter = (AbstractDbParameter)derivedParameters[i++];
\r
697 if (derivedParameter.IsSpecial) {
\r
698 // derived parameter is special - never appears in user parameters or user values
\r
699 if (!hasUserParams && !addedSpecialParams) {
\r
700 addedSpecialParams = true;
\r
705 for (;curDerivedPos < i;curDerivedPos++)
\r
708 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
713 if (!hasUserParams && addedSpecialParams)
\r
716 sb.Append(sql,queryCurrentPosition,sql.Length - queryCurrentPosition);
\r
717 return curUserPos - userParamsStartPosition;
\r
720 protected virtual AbstractDbParameter GetUserParameter(string parameterName, IList userParametersList, int userParametersListPosition)
\r
722 if (userParametersListPosition < userParametersList.Count) {
\r
723 AbstractDbParameter param = (AbstractDbParameter)userParametersList[userParametersListPosition];
\r
724 if (param.Placeholder == parameterName)
\r
730 protected virtual AbstractDbParameter GetReturnParameter (IList userParametersList)
\r
732 AbstractDbParameter param = GetUserParameter ("?", userParametersList, 0);
\r
734 if (param != null && param.Direction == ParameterDirection.ReturnValue)
\r
740 int PrepareSimpleQuery(StringBuilder sb, string query, IList userParametersList, int userParametersListStart)
\r
742 int queryCurrentPosition = 0;
\r
743 int userParametersListPosition = userParametersListStart;
\r
745 if (userParametersList.Count > 0) {
\r
746 for (SimpleMatch m = ParameterRegExp.Match(query);
\r
747 m.Success;m = m.NextMatch()) {
\r
749 SimpleCapture parameterCapture = m;
\r
750 sb.Append(query,queryCurrentPosition,parameterCapture.Index - queryCurrentPosition);
\r
752 // advance in query
\r
753 queryCurrentPosition = parameterCapture.Index + parameterCapture.Length;
\r
755 AbstractDbParameter userParameter = GetUserParameter(parameterCapture.Value, userParametersList, userParametersListPosition);
\r
757 if (userParameter != null) {
\r
758 if (IsNullParameter(userParameter)) {
\r
760 NullParametersInPrepare = true;
\r
764 InternalParameters.Add(userParameter);
\r
766 // advance in user parameters
\r
767 userParametersListPosition++;
\r
770 sb.Append(parameterCapture.Value);
\r
775 sb.Append(query,queryCurrentPosition,query.Length - queryCurrentPosition);
\r
776 int userParamsConsumed = userParametersListPosition - userParametersListStart;
\r
778 if ((Behavior & CommandBehavior.KeyInfo) == 0)
\r
779 return userParamsConsumed;
\r
781 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
782 if (connection == null)
\r
783 return userParamsConsumed;
\r
785 string dbname = connection.JdbcConnection.getMetaData().getDatabaseProductName();
\r
786 if (dbname == "Microsoft SQL Server") { //must add "FOR BROWSE" for selects
\r
787 #if USE_DOTNET_REGEX
\r
788 if (!SqlStatementsHelper.ForBrowseStatementReqExp.IsMatch(query))
\r
789 sb.Append(" FOR BROWSE");
\r
791 if (!SqlStatementsHelper.ForBrowseStatementReqExp.matcher ((java.lang.CharSequence)(object)query).find ())
\r
792 sb.Append (" FOR BROWSE");
\r
796 return userParamsConsumed;
\r
799 protected virtual bool IsNullParameter(AbstractDbParameter parameter)
\r
801 return ((parameter.Value == null || parameter.Value == DBNull.Value) && !parameter.IsDbTypeSet);
\r
804 protected virtual void PrepareInternalParameters()
\r
806 InternalParameters.Clear();
\r
809 protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
\r
811 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
812 if (connection == null) {
\r
813 throw ExceptionHelper.ConnectionNotInitialized("ExecuteReader");
\r
816 IDbTransaction transaction = Transaction;
\r
817 if ((transaction != null && transaction.Connection != connection) ||
\r
818 (transaction == null && !connection.JdbcConnection.getAutoCommit())) {
\r
819 throw ExceptionHelper.TransactionNotInitialized();
\r
822 connection.IsExecuting = true;
\r
825 Behavior = behavior;
\r
827 PrepareInternalParameters();
\r
830 // For SchemaOnly there is no need for statement execution
\r
831 if (Behavior != CommandBehavior.SchemaOnly) {
\r
832 _recordsAffected = -1;
\r
834 // FIXME: this causes SP in MS Sql Server to create no mor than one row.
\r
835 if ((Behavior & CommandBehavior.SingleRow) != 0) {
\r
836 _statement.setFetchSize(1);
\r
839 if(_statement is PreparedStatement) {
\r
840 BindParameters(InternalParameters);
\r
841 _hasResultSet = ((PreparedStatement)_statement).execute();
\r
844 _hasResultSet =_statement.execute(_javaCommandText);
\r
847 if (!_hasResultSet) {
\r
848 int updateCount = _statement.getUpdateCount();
\r
849 if (updateCount >= 0) {
\r
850 AccumulateRecordsAffected(updateCount);
\r
851 _hasResultSet = true; //play as if we have resultset
\r
856 connection.IsFetching = true;
\r
858 _currentReader = CreateReader();
\r
860 catch(Exception e) {
\r
861 connection.IsFetching = false;
\r
864 return _currentReader;
\r
866 catch(SQLException e) {
\r
867 throw CreateException(e);
\r
870 connection.IsExecuting = false;
\r
871 NullParametersInPrepare = false;
\r
875 public override void Prepare()
\r
877 ((AbstractDBConnection)Connection).IsExecuting = true;
\r
880 _explicitPrepare = true;
\r
883 ((AbstractDBConnection)Connection).IsExecuting = false;
\r
887 private void PrepareInternal()
\r
889 if ((Connection == null) || (Connection.State != ConnectionState.Open)) {
\r
890 throw ExceptionHelper.ConnectionNotOpened("Prepare",(Connection != null) ? Connection.State.ToString() : "");
\r
893 if (IsCommandPrepared) {
\r
894 // maybe we have to prepare the command again
\r
895 bool hasNullParameters = false;
\r
896 for(int i = 0; (i < Parameters.Count) && !hasNullParameters; i++) {
\r
897 AbstractDbParameter parameter = (AbstractDbParameter)Parameters[i];
\r
898 if (IsNullParameter(parameter)) {
\r
899 // if we still have null parameters - have to prepare agail
\r
900 IsCommandPrepared = false;
\r
901 hasNullParameters = true;
\r
905 if (!NullParametersInPrepare && hasNullParameters) {
\r
906 // if we prepeared using null parameters and now there is no null parameters - need to prepare again
\r
907 IsCommandPrepared = false;
\r
911 if (!IsCommandPrepared) {
\r
913 _javaCommandText = PrepareCommandTextAndParameters();
\r
915 java.sql.Connection jdbcCon = _connection.JdbcConnection;
\r
917 // For SchemaOnly we just prepare statement (for future use in GetSchemaTable)
\r
918 if (Behavior == CommandBehavior.SchemaOnly) {
\r
919 if (CommandType == CommandType.StoredProcedure)
\r
920 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
922 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
926 if (CommandType == CommandType.StoredProcedure)
\r
927 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
929 int internalParametersCount = InternalParameters.Count;
\r
930 if ( internalParametersCount > 0) {
\r
931 bool hasOnlyInputParameters = true;
\r
932 for(int i=0; i < internalParametersCount; i++) {
\r
933 AbstractDbParameter internalParameter = (AbstractDbParameter)InternalParameters[i];
\r
934 if (IsNullParameter(internalParameter)) {
\r
935 NullParametersInPrepare = true;
\r
938 if ((internalParameter.Direction & ParameterDirection.Output) != 0){
\r
939 hasOnlyInputParameters = false;
\r
943 if (hasOnlyInputParameters) {
\r
944 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
947 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
951 if (_explicitPrepare) {
\r
952 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
955 _statement = jdbcCon.createStatement();
\r
959 IsCommandPrepared = true;
\r
963 protected void BindParameters(ArrayList parameters)
\r
965 for(int parameterIndex = 0; parameterIndex < parameters.Count; parameterIndex++) {
\r
966 AbstractDbParameter parameter = (AbstractDbParameter)parameters[parameterIndex];
\r
967 switch (parameter.Direction) {
\r
968 case ParameterDirection.Input :
\r
969 BindInputParameter(parameter,parameterIndex);
\r
971 case ParameterDirection.InputOutput:
\r
972 BindInputParameter(parameter,parameterIndex);
\r
973 BindOutputParameter(parameter,parameterIndex);
\r
975 case ParameterDirection.Output :
\r
976 BindOutputParameter(parameter,parameterIndex);
\r
978 case ParameterDirection.ReturnValue :
\r
979 BindOutputParameter(parameter,parameterIndex);
\r
985 protected virtual void BindInputParameter(AbstractDbParameter parameter, int parameterIndex)
\r
987 object value = parameter.ConvertedValue;
\r
988 // java parameters are 1 based, while .net are 0 based
\r
990 PreparedStatement preparedStatement = ((PreparedStatement)_statement);
\r
992 switch ((DbConvert.JavaSqlTypes)parameter.JdbcType) {
\r
993 case DbConvert.JavaSqlTypes.DATALINK:
\r
994 case DbConvert.JavaSqlTypes.DISTINCT:
\r
995 case DbConvert.JavaSqlTypes.JAVA_OBJECT:
\r
996 case DbConvert.JavaSqlTypes.OTHER:
\r
997 case DbConvert.JavaSqlTypes.REF:
\r
998 case DbConvert.JavaSqlTypes.STRUCT: {
\r
999 preparedStatement.setObject(parameterIndex, value, (int)parameter.JdbcType);
\r
1004 if ((value is DBNull) || (value == null)) {
\r
1005 preparedStatement.setNull(parameterIndex, (int)((AbstractDbParameter)parameter).JdbcType);
\r
1007 else if (value is long) {
\r
1008 preparedStatement.setLong(parameterIndex, (long)value);
\r
1010 else if (value is byte[]) {
\r
1011 if (((byte[])value).Length <= 4000) {
\r
1012 preparedStatement.setBytes(parameterIndex, vmw.common.TypeUtils.ToSByteArray((byte[]) value));
\r
1015 InputStream iStream=new ByteArrayInputStream(vmw.common.TypeUtils.ToSByteArray((byte[]) value));
\r
1016 preparedStatement.setBinaryStream(parameterIndex,iStream,((byte[])value).Length);
\r
1019 else if (value is byte) {
\r
1020 preparedStatement.setByte(parameterIndex, (sbyte)(byte)value);
\r
1022 else if (value is char[]) {
\r
1023 Reader reader = new CharArrayReader((char[])value);
\r
1024 preparedStatement.setCharacterStream(parameterIndex,reader,((char[])value).Length);
\r
1026 else if (value is bool) {
\r
1027 preparedStatement.setBoolean(parameterIndex, (bool) value);
\r
1029 else if (value is char) {
\r
1030 preparedStatement.setString(parameterIndex, ((char)value).ToString());
\r
1032 else if (value is DateTime) {
\r
1033 switch ((DbConvert.JavaSqlTypes)parameter.JdbcType) {
\r
1035 case DbConvert.JavaSqlTypes.TIMESTAMP:
\r
1036 preparedStatement.setTimestamp(parameterIndex,DbConvert.ClrTicksToJavaTimestamp(((DateTime)value).Ticks));
\r
1038 case DbConvert.JavaSqlTypes.TIME:
\r
1039 preparedStatement.setTime(parameterIndex,DbConvert.ClrTicksToJavaTime(((DateTime)value).Ticks));
\r
1041 case DbConvert.JavaSqlTypes.DATE:
\r
1042 preparedStatement.setDate(parameterIndex,DbConvert.ClrTicksToJavaDate(((DateTime)value).Ticks));
\r
1046 else if (value is TimeSpan) {
\r
1047 if (parameter.JdbcType == (int)DbConvert.JavaSqlTypes.TIMESTAMP)
\r
1048 preparedStatement.setTimestamp(parameterIndex,DbConvert.ClrTicksToJavaTimestamp(((TimeSpan)value).Ticks));
\r
1050 preparedStatement.setTime(parameterIndex,DbConvert.ClrTicksToJavaTime(((TimeSpan)value).Ticks));
\r
1052 else if (value is Decimal) {
\r
1053 preparedStatement.setBigDecimal(parameterIndex, vmw.common.PrimitiveTypeUtils.DecimalToBigDecimal((Decimal) value));
\r
1055 else if (value is double) {
\r
1056 preparedStatement.setDouble(parameterIndex, (double)value);
\r
1058 else if (value is float) {
\r
1059 preparedStatement.setFloat(parameterIndex, (float)value);
\r
1061 else if (value is int) {
\r
1062 preparedStatement.setInt(parameterIndex, (int)value);
\r
1064 else if (value is string) {
\r
1065 //can not be done for inout params, due to Oracle problem with FIXED_CHAR out param fetching
\r
1066 if (parameter.Direction == ParameterDirection.Input &&
\r
1067 preparedStatement is Mainsoft.Data.Jdbc.Providers.IPreparedStatement &&
\r
1068 (DbConvert.JavaSqlTypes)parameter.JdbcType == DbConvert.JavaSqlTypes.CHAR) {
\r
1069 ((Mainsoft.Data.Jdbc.Providers.IPreparedStatement)preparedStatement)
\r
1070 .setChar(parameterIndex, (string)value);
\r
1073 preparedStatement.setString(parameterIndex, (string)value);
\r
1075 else if (value is Guid) {
\r
1076 preparedStatement.setString(parameterIndex, value.ToString());
\r
1078 else if (value is short) {
\r
1079 preparedStatement.setShort(parameterIndex, (short)value);
\r
1081 else if (value is sbyte) {
\r
1082 preparedStatement.setByte(parameterIndex, (sbyte)value);
\r
1085 preparedStatement.setObject(parameterIndex, value);
\r
1089 protected virtual void BindOutputParameter(AbstractDbParameter parameter, int parameterIndex)
1091 parameter.Validate();
1092 int jdbcType = (int)parameter.JdbcType;
\r
1093 // java parameters are 1 based, while .net are 0 based
\r
1096 CallableStatement callableStatement = ((CallableStatement)_statement);
\r
1098 // the scale has a meening only in DECIMAL and NUMERIC parameters
\r
1099 if (jdbcType == Types.DECIMAL || jdbcType == Types.NUMERIC) {
\r
1100 if(parameter.DbType == DbType.Currency) {
\r
1101 callableStatement.registerOutParameter(parameterIndex, jdbcType, 4);
\r
1104 callableStatement.registerOutParameter(parameterIndex, jdbcType, parameter.Scale);
\r
1108 callableStatement.registerOutParameter(parameterIndex, jdbcType);
\r
1112 private void FillOutputParameters()
1114 if (!(_statement is CallableStatement)) {
1117 for(int i = 0; i < InternalParameters.Count; i++) {
1118 AbstractDbParameter parameter = (AbstractDbParameter)InternalParameters[i];
1119 ParameterDirection direction = parameter.Direction;
1120 if (((direction & ParameterDirection.Output) != 0) && !SkipParameter(parameter)) {
1121 FillOutputParameter(parameter, i);
1123 // drop jdbc type of out parameter, since it possibly was updated in ExecuteReader
1124 parameter.IsJdbcTypeSet = false;
1128 protected virtual void FillOutputParameter(DbParameter parameter, int index)
\r
1130 CallableStatement callableStatement = (CallableStatement)_statement;
\r
1131 ParameterMetadataWrapper parameterMetadataWrapper = null;
\r
1132 // FIXME wait for other drivers to implement
\r
1134 // parameterMetadataWrapper = new ParameterMetadataWrapper(callableStatement.getParameterMetaData());
\r
1137 // // suppress error : ms driver for sql server does not implement getParameterMetaData
\r
1138 // // suppress exception : ms driver for sql server does not implement getParameterMetaData
\r
1140 DbConvert.JavaSqlTypes javaSqlType = (DbConvert.JavaSqlTypes)((AbstractDbParameter)parameter).JdbcType;
\r
1142 parameter.Value = DbConvert.JavaResultSetToClrWrapper(callableStatement,index,javaSqlType,parameter.Size,parameterMetadataWrapper);
\r
1144 catch(java.sql.SQLException e) {
\r
1145 throw CreateException(e);
\r
1149 // AbstractDbCommand acts as IEnumerator over JDBC statement
\r
1150 // AbstractDbCommand.NextResultSet corresponds to IEnumerator.MoveNext
\r
1151 protected internal virtual bool NextResultSet()
\r
1153 if (!_hasResultSet)
\r
1158 _hasResultSet = _statement.getMoreResults();
\r
1159 if (_hasResultSet)
\r
1161 int updateCount = _statement.getUpdateCount();
\r
1162 if (updateCount < 0)
\r
1165 AccumulateRecordsAffected(updateCount);
\r
1168 catch (SQLException e) {
\r
1169 throw CreateException(e);
\r
1172 _currentResultSet = null;
\r
1176 private void AccumulateRecordsAffected(int updateCount)
\r
1178 if (_recordsAffected < 0) {
\r
1179 _recordsAffected = updateCount;
\r
1182 _recordsAffected += updateCount;
\r
1186 internal void OnReaderClosed(object reader)
\r
1189 if (Connection != null) {
\r
1190 ((AbstractDBConnection)Connection).RemoveReference(reader);
\r
1191 ((AbstractDBConnection)Connection).IsFetching = false;
\r
1192 if ((Behavior & CommandBehavior.CloseConnection) != 0) {
\r
1193 Connection.Close();
\r
1198 internal void CloseInternal()
\r
1200 if (Behavior != CommandBehavior.SchemaOnly) {
\r
1201 if (_statement != null) {
\r
1202 while (NextResultSet()) {
\r
1204 FillOutputParameters();
\r
1207 _currentReader = null;
\r
1211 protected override void Dispose(bool disposing)
\r
1216 base.Dispose(disposing);
\r
1219 private void CleanUp()
\r
1221 if (_currentReader != null) {
\r
1222 // we must preserve statement object until we have an associated reader object that might access it.
\r
1225 if (Connection != null) {
\r
1226 ((AbstractDBConnection)Connection).RemoveReference(this);
\r
1228 if (_statement != null) {
\r
1229 _statement.close();
\r
1230 _statement = null;
\r
1232 IsCommandPrepared = false;
\r
1233 _internalParameters = null;
\r
1234 _currentResultSet = null;
\r
1237 internal void OnSchemaChanging()
\r
1241 #endregion // Methods
\r
1243 #region ICloneable Members
\r
1245 public virtual object Clone() {
\r
1246 AbstractDbCommand target = (AbstractDbCommand)MemberwiseClone();
\r
1247 target._statement = null;
\r
1248 target._isCommandPrepared = false;
\r
1249 target._internalParameters = null;
\r
1250 target._javaCommandText = null;
\r
1251 target._recordsAffected = -1;
\r
1252 target._currentResultSet = null;
\r
1253 target._currentReader = null;
\r
1254 target._nullParametersInPrepare = false;
\r
1255 target._hasResultSet = false;
\r
1256 target._explicitPrepare = false;
\r
1257 if (Parameters != null && Parameters.Count > 0) {
\r
1258 target._parameters = CreateParameterCollection(target);
\r
1259 for(int i=0 ; i < Parameters.Count; i++) {
\r
1260 target.Parameters.Add(((AbstractDbParameter)Parameters[i]).Clone());
\r