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 protected virtual string InternalCommandText {
\r
227 get { return CommandText; }
\r
228 //set { CommandText = value; }
\r
231 internal CommandBehavior Behavior
\r
233 get { return _behavior; }
\r
234 set { _behavior = value; }
\r
237 bool IsCommandPrepared
\r
239 get { return _isCommandPrepared; }
\r
240 set { _isCommandPrepared = value; }
\r
243 bool NullParametersInPrepare
\r
245 get { return _nullParametersInPrepare; }
\r
246 set { _nullParametersInPrepare = value; }
\r
249 protected ArrayList InternalParameters
\r
252 if (_internalParameters == null) {
\r
253 _internalParameters = new ArrayList();
\r
255 return _internalParameters;
\r
259 // Number of records affected by execution of batch statement
\r
260 // -1 for SELECT statements.
\r
261 internal int RecordsAffected
\r
264 return _recordsAffected;
\r
268 // AbstractDbCommand acts as IEnumerator over JDBC statement
\r
269 // AbstractDbCommand.CurrentResultSet corresponds to IEnumerator.Current
\r
270 protected internal virtual ResultSet CurrentResultSet
\r
274 if (_currentResultSet == null && _hasResultSet) {
\r
275 _currentResultSet = _statement.getResultSet();
\r
277 return _currentResultSet;
\r
279 catch(SQLException e) {
\r
280 throw new Exception(e.Message, e);
\r
285 protected internal java.sql.Statement Statement
\r
287 get { return _statement; }
\r
289 #if USE_DOTNET_REGEX
\r
290 protected virtual Regex StoredProcedureRegExp
\r
292 protected virtual Pattern StoredProcedureRegExp
\r
295 get { return SqlStatementsHelper.SimpleParameterStoredProcedureRegExp; }
\r
298 protected virtual SimpleRegex ParameterRegExp
\r
300 get { return SqlStatementsHelper.SimpleParameterRegExp; }
\r
303 #endregion // Properties
\r
307 protected abstract DbParameter CreateParameterInternal();
\r
309 protected abstract void CheckParameters();
\r
311 protected abstract DbDataReader CreateReader();
\r
313 protected abstract DbParameterCollection CreateParameterCollection(AbstractDbCommand parent);
\r
315 protected internal abstract SystemException CreateException(SQLException e);
\r
317 public override int ExecuteNonQuery ()
319 IDataReader reader = null;
321 reader = ExecuteReader ();
327 return reader.RecordsAffected;
330 public override object ExecuteScalar ()
332 IDataReader reader = ExecuteReader(CommandBehavior.SequentialAccess);
336 if (reader.FieldCount > 0 && reader.Read ())
337 return reader.GetValue (0);
339 while (reader.NextResult ());
346 public virtual void ResetCommandTimeout ()
348 _commandTimeout = 30;
351 public override void Cancel()
\r
354 if (_statement != null)
\r
355 _statement.cancel();
\r
358 // MSDN says : "If there is nothing to cancel, nothing happens.
\r
359 // However, if there is a command in process, and the attempt to cancel fails,
\r
360 // no exception is generated."
\r
364 protected virtual bool SkipParameter(DbParameter parameter)
\r
369 protected sealed override DbParameter CreateDbParameter()
\r
371 return CreateParameterInternal();
\r
374 internal ArrayList DeriveParameters(string procedureName, bool throwIfNotExist)
\r
377 ArrayList col = _procedureColumnCache.GetProcedureColumns((AbstractDBConnection)Connection, procedureName, this);
\r
379 if (throwIfNotExist)
\r
380 throw ExceptionHelper.NoStoredProcedureExists(procedureName);
\r
381 col = new ArrayList();
\r
386 catch(SQLException e) {
\r
387 throw CreateException(e);
\r
391 string CreateTableDirectCommandText(string tableNames) {
\r
392 string forBrowse = String.Empty;
\r
393 if ((Behavior & CommandBehavior.KeyInfo) != 0) {
\r
394 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
395 if (connection != null) {
\r
396 string dbname = connection.JdbcConnection.getMetaData().getDatabaseProductName();
\r
397 if (dbname == "Microsoft SQL Server") //must add "FOR BROWSE" for selects
\r
398 forBrowse = " FOR BROWSE";
\r
402 string[] names = tableNames.Split(',');
\r
403 StringBuilder sb = new StringBuilder();
\r
405 for(int i = 0; i < names.Length; i++) {
\r
406 sb.Append("SELECT * FROM ");
\r
407 sb.Append(names[i]);
\r
408 sb.Append(forBrowse);
\r
412 if(names.Length <= 1) {
\r
413 sb.Remove(sb.Length - 1,1);
\r
415 return sb.ToString();
\r
418 private string PrepareCommandTextAndParameters()
\r
420 NullParametersInPrepare = false;
\r
421 switch (CommandType) {
\r
422 case CommandType.TableDirect :
\r
423 return CreateTableDirectCommandText(CommandText);
\r
424 case CommandType.StoredProcedure :
\r
425 return CreateStoredProcedureCommandTextSimple (InternalCommandText, Parameters, DeriveParameters (InternalCommandText, false));
\r
426 case CommandType.Text :
\r
428 int userParametersPosition = 0;
\r
429 int charsConsumed = 0;
\r
430 StringBuilder sb = new StringBuilder(CommandText.Length);
\r
432 for (SimpleMatch match = SqlStatementsHelper.CompoundStatementSplitterReqExp.Match(CommandText);
\r
434 match = match.NextMatch()) {
\r
436 int length = match.Length;
\r
441 int start = match.Index;
\r
442 string value = match.Value;
\r
444 sb.Append(CommandText, charsConsumed, start-charsConsumed);
\r
445 charsConsumed = start + length;
\r
447 #if USE_DOTNET_REGEX
\r
448 Match storedProcMatch = StoredProcedureRegExp.Match(value);
\r
449 // count parameters for all kinds of simple statements
\r
450 userParametersPosition +=
\r
451 (storedProcMatch.Success) ?
\r
452 // statement is stored procedure call
\r
453 CreateStoredProcedureCommandText(sb, value, storedProcMatch, Parameters, userParametersPosition) :
\r
454 // statement is a simple SQL query
\r
455 PrepareSimpleQuery(sb, value, Parameters, userParametersPosition);
\r
457 Matcher storedProcMatch = StoredProcedureRegExp.matcher((java.lang.CharSequence)(object)value);
\r
458 userParametersPosition +=
\r
459 (storedProcMatch.find()) ?
\r
460 // statement is stored procedure call
\r
461 CreateStoredProcedureCommandText(sb, value, storedProcMatch, Parameters, userParametersPosition) :
\r
462 // statement is a simple SQL query
\r
463 PrepareSimpleQuery(sb, value, Parameters, userParametersPosition);
\r
467 sb.Append(CommandText, charsConsumed, CommandText.Length-charsConsumed);
\r
469 return sb.ToString();
\r
474 string CreateStoredProcedureCommandTextSimple(string procedureName, IDataParameterCollection userParams, IList derivedParams) {
\r
475 StringBuilder sb = new StringBuilder();
\r
477 int curUserPos = 0;
\r
478 int curDerivedPos = 0;
\r
479 bool addParas = true;
\r
480 string trimedProcedureName = (procedureName != null) ? procedureName.TrimEnd() : String.Empty;
\r
481 if (trimedProcedureName.Length > 0 && trimedProcedureName[trimedProcedureName.Length-1] == ')')
\r
484 AbstractDbParameter derivedParam = (derivedParams.Count > 0) ? (AbstractDbParameter)derivedParams[curDerivedPos] : null;
\r
485 if (derivedParam != null) {
\r
486 if (derivedParam.Direction == ParameterDirection.ReturnValue)
\r
489 derivedParam = null; //play as if there is no retval parameter
\r
491 AbstractDbParameter returnValueParameter = GetReturnParameter (userParams);
\r
492 if (returnValueParameter != null) {
\r
494 InternalParameters.Add(returnValueParameter);
\r
495 sb.Append("{? = call ");
\r
497 if (derivedParam != null && !returnValueParameter.IsDbTypeSet) {
\r
498 returnValueParameter.JdbcType = derivedParam.JdbcType;
\r
502 sb.Append("{call ");
\r
505 sb.Append(procedureName);
\r
509 bool needComma = false;
\r
510 for (int i = curDerivedPos; i < derivedParams.Count; i++) {
\r
511 AbstractDbParameter derivedParameter = (AbstractDbParameter)derivedParams[curDerivedPos++];
\r
513 bool addParam = false;
\r
515 if (derivedParameter.IsSpecial) {
\r
516 // derived parameter is special - never appears in user parameters or user values
\r
517 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
521 AbstractDbParameter userParameter = GetUserParameter(derivedParameter.Placeholder, userParams, curUserPos);
\r
522 if (userParameter != null) {
\r
524 InternalParameters.Add(userParameter);
\r
527 if (derivedParameter != null && !userParameter.IsDbTypeSet) {
\r
528 userParameter.JdbcType = derivedParameter.JdbcType;
\r
543 for (int i = curUserPos; i < userParams.Count; i++) {
\r
549 AbstractDbParameter userParameter = (AbstractDbParameter)userParams[curUserPos++];
\r
550 InternalParameters.Add(userParameter);
\r
558 return sb.ToString();
\r
562 /// We suppose that user parameters are in the same order as devived parameters except the special cases
\r
563 /// (return value, oracle ref cursors etc.)
\r
565 //protected virtual string CreateStoredProcedureCommandText(string procedureName, IList userParametersList, int userParametersListStart/*, int userParametersListCount*/, string[] userValuesList, ArrayList derivedParametersList)
\r
566 #if USE_DOTNET_REGEX
\r
567 int CreateStoredProcedureCommandText(StringBuilder sb, string sql, Match match, IDataParameterCollection userParams, int userParamsStartPosition)
\r
569 int CreateStoredProcedureCommandText(StringBuilder sb, string sql, Matcher match, IDataParameterCollection userParams, int userParamsStartPosition)
\r
572 int curUserPos = userParamsStartPosition;
\r
573 #if USE_DOTNET_REGEX
\r
574 Group procNameGroup = null;
\r
576 for (Match procNameMatch = match; procNameMatch.Success; procNameMatch = procNameMatch.NextMatch()){
\r
577 procNameGroup = match.Groups["PROCNAME"];
\r
578 if (!procNameGroup.Success) {
\r
583 if (procNameGroup == null || !procNameGroup.Success)
\r
584 throw new ArgumentException("Not a stored procedure call: '{0}'", sql);
\r
586 ArrayList derivedParameters = DeriveParameters(procNameGroup.Value, false);
\r
588 ArrayList derivedParameters = DeriveParameters(match.group(2).Trim(), false);
\r
590 int curDerivedPos = 0;
\r
592 AbstractDbParameter retValderivedParameter = curDerivedPos < derivedParameters.Count ?
\r
593 (AbstractDbParameter)derivedParameters[curDerivedPos] : null;
\r
594 if (retValderivedParameter != null && retValderivedParameter.Direction == ParameterDirection.ReturnValue)
\r
597 int queryCurrentPosition = 0;
\r
599 #if USE_DOTNET_REGEX
\r
600 for (Match retValMatch = match; retValMatch.Success; retValMatch = retValMatch.NextMatch()){
\r
601 Group retval = retValMatch.Groups["RETVAL"];
\r
602 if (!retval.Success) {
\r
606 int retvalIndex = retval.Index;
\r
607 string retvalValue = retval.Value;
\r
608 int retvalLength = retval.Length;
\r
610 int retvalIndex = match.start(1);
\r
611 for (;retvalIndex >= 0;) {
\r
612 string retvalValue = match.group(1);
\r
613 int retvalLength = retvalValue.Length;
\r
616 sb.Append(sql, queryCurrentPosition, retvalIndex);
\r
617 AbstractDbParameter userParameter = GetUserParameter(retvalValue, userParams, curUserPos);
\r
618 if (userParameter != null) {
\r
620 InternalParameters.Add(userParameter);
\r
622 if (retValderivedParameter != null && !userParameter.IsDbTypeSet) {
\r
623 userParameter.JdbcType = retValderivedParameter.JdbcType;
\r
629 sb.Append(retvalValue);
\r
632 queryCurrentPosition = (retvalIndex + retvalLength);
\r
637 #if USE_DOTNET_REGEX
\r
638 sb.Append(sql, queryCurrentPosition, procNameGroup.Index + procNameGroup.Length - queryCurrentPosition);
\r
639 queryCurrentPosition = procNameGroup.Index + procNameGroup.Length;
\r
641 sb.Append(sql, queryCurrentPosition, match.end(2) - queryCurrentPosition);
\r
642 queryCurrentPosition = match.end(2);
\r
645 bool hasUserParams = false;
\r
647 #if USE_DOTNET_REGEX
\r
648 must rewrite the regex to not parse params to have single code with java regex
\r
650 int paramsStart = match.start(3);
\r
651 if (paramsStart >= 0) {
\r
654 hasUserParams = true;
\r
655 sb.Append(sql,queryCurrentPosition,paramsStart - queryCurrentPosition);
\r
656 queryCurrentPosition = paramsStart;
\r
658 for (SimpleMatch m = SqlStatementsHelper.ProcedureParameterSplitterReqExp.Match(match.group(3));
\r
659 m.Success;m = m.NextMatch()) {
\r
661 SimpleCapture parameterCapture = m;
\r
662 sb.Append(sql,queryCurrentPosition,paramsStart + parameterCapture.Index - queryCurrentPosition);
\r
664 // advance in query
\r
665 queryCurrentPosition = paramsStart + parameterCapture.Index + parameterCapture.Length;
\r
667 AbstractDbParameter derivedParameter = curDerivedPos < derivedParameters.Count ?
\r
668 (AbstractDbParameter)derivedParameters[curDerivedPos++] : null;
\r
670 //check for special params
\r
671 while (derivedParameter != null && derivedParameter.IsSpecial) {
\r
672 // derived parameter is special - never appears in user parameters or user values
\r
673 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
677 derivedParameter = curDerivedPos < derivedParameters.Count ?
\r
678 (AbstractDbParameter)derivedParameters[curDerivedPos++] : null;
\r
681 AbstractDbParameter userParameter = GetUserParameter(parameterCapture.Value.Trim(), userParams, curUserPos);
\r
683 if (userParameter != null) {
\r
685 InternalParameters.Add(userParameter);
\r
686 if (derivedParameter != null && !userParameter.IsDbTypeSet) {
\r
687 userParameter.JdbcType = derivedParameter.JdbcType;
\r
689 // advance in user parameters
\r
693 sb.Append(parameterCapture.Value);
\r
698 bool addedSpecialParams = false;
\r
700 for (int i = curDerivedPos; i < derivedParameters.Count;) {
\r
701 AbstractDbParameter derivedParameter = (AbstractDbParameter)derivedParameters[i++];
\r
702 if (derivedParameter.IsSpecial) {
\r
703 // derived parameter is special - never appears in user parameters or user values
\r
704 if (!hasUserParams && !addedSpecialParams) {
\r
705 addedSpecialParams = true;
\r
710 for (;curDerivedPos < i;curDerivedPos++)
\r
713 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
718 if (!hasUserParams && addedSpecialParams)
\r
721 sb.Append(sql,queryCurrentPosition,sql.Length - queryCurrentPosition);
\r
722 return curUserPos - userParamsStartPosition;
\r
725 protected virtual AbstractDbParameter GetUserParameter(string parameterName, IList userParametersList, int userParametersListPosition)
\r
727 if (userParametersListPosition < userParametersList.Count) {
\r
728 AbstractDbParameter param = (AbstractDbParameter)userParametersList[userParametersListPosition];
\r
729 if (param.Placeholder == parameterName)
\r
735 protected virtual AbstractDbParameter GetReturnParameter (IList userParametersList)
\r
737 AbstractDbParameter param = GetUserParameter ("?", userParametersList, 0);
\r
739 if (param != null && param.Direction == ParameterDirection.ReturnValue)
\r
745 int PrepareSimpleQuery(StringBuilder sb, string query, IList userParametersList, int userParametersListStart)
\r
747 int queryCurrentPosition = 0;
\r
748 int userParametersListPosition = userParametersListStart;
\r
750 if (userParametersList.Count > 0) {
\r
751 for (SimpleMatch m = ParameterRegExp.Match(query);
\r
752 m.Success;m = m.NextMatch()) {
\r
754 SimpleCapture parameterCapture = m;
\r
755 sb.Append(query,queryCurrentPosition,parameterCapture.Index - queryCurrentPosition);
\r
757 // advance in query
\r
758 queryCurrentPosition = parameterCapture.Index + parameterCapture.Length;
\r
760 AbstractDbParameter userParameter = GetUserParameter(parameterCapture.Value, userParametersList, userParametersListPosition);
\r
762 if (userParameter != null) {
\r
763 if (IsNullParameter(userParameter)) {
\r
765 NullParametersInPrepare = true;
\r
769 InternalParameters.Add(userParameter);
\r
771 // advance in user parameters
\r
772 userParametersListPosition++;
\r
775 sb.Append(parameterCapture.Value);
\r
780 sb.Append(query,queryCurrentPosition,query.Length - queryCurrentPosition);
\r
781 int userParamsConsumed = userParametersListPosition - userParametersListStart;
\r
783 if ((Behavior & CommandBehavior.KeyInfo) == 0)
\r
784 return userParamsConsumed;
\r
786 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
787 if (connection == null)
\r
788 return userParamsConsumed;
\r
790 string dbname = connection.JdbcConnection.getMetaData().getDatabaseProductName();
\r
791 if (dbname == "Microsoft SQL Server") { //must add "FOR BROWSE" for selects
\r
792 #if USE_DOTNET_REGEX
\r
793 if (!SqlStatementsHelper.ForBrowseStatementReqExp.IsMatch(query))
\r
794 sb.Append(" FOR BROWSE");
\r
796 if (!SqlStatementsHelper.ForBrowseStatementReqExp.matcher ((java.lang.CharSequence)(object)query).find ())
\r
797 sb.Append (" FOR BROWSE");
\r
801 return userParamsConsumed;
\r
804 protected virtual bool IsNullParameter(AbstractDbParameter parameter)
\r
806 return ((parameter.Value == null || parameter.Value == DBNull.Value) && !parameter.IsDbTypeSet);
\r
809 protected virtual void PrepareInternalParameters()
\r
811 InternalParameters.Clear();
\r
814 protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
\r
816 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
817 if (connection == null) {
\r
818 throw ExceptionHelper.ConnectionNotInitialized("ExecuteReader");
\r
821 connection.IsExecuting = true;
\r
824 IDbTransaction transaction = Transaction;
\r
825 if ((transaction != null && transaction.Connection != connection) ||
\r
826 (transaction == null && !connection.JdbcConnection.getAutoCommit ())) {
\r
827 throw ExceptionHelper.TransactionNotInitialized ();
\r
830 Behavior = behavior;
\r
832 PrepareInternalParameters();
\r
835 // For SchemaOnly there is no need for statement execution
\r
836 if (Behavior != CommandBehavior.SchemaOnly) {
\r
837 _recordsAffected = -1;
\r
839 // FIXME: this causes SP in MS Sql Server to create no mor than one row.
\r
840 if ((Behavior & CommandBehavior.SingleRow) != 0) {
\r
841 _statement.setMaxRows (1);
\r
844 if(_statement is PreparedStatement) {
\r
845 BindParameters(InternalParameters);
\r
846 _hasResultSet = ((PreparedStatement)_statement).execute();
\r
849 _hasResultSet =_statement.execute(_javaCommandText);
\r
852 if (!_hasResultSet) {
\r
853 int updateCount = _statement.getUpdateCount();
\r
854 if (updateCount >= 0) {
\r
855 AccumulateRecordsAffected(updateCount);
\r
856 _hasResultSet = true; //play as if we have resultset
\r
861 connection.IsFetching = true;
\r
863 _currentReader = CreateReader();
\r
865 catch(Exception e) {
\r
866 connection.IsFetching = false;
\r
869 return _currentReader;
\r
871 catch(SQLException e) {
\r
872 throw CreateException(e);
\r
875 connection.IsExecuting = false;
\r
876 NullParametersInPrepare = false;
\r
880 public override void Prepare()
\r
882 ((AbstractDBConnection)Connection).IsExecuting = true;
\r
885 _explicitPrepare = true;
\r
888 ((AbstractDBConnection)Connection).IsExecuting = false;
\r
892 private void PrepareInternal()
\r
894 if ((Connection == null) || (Connection.State != ConnectionState.Open)) {
\r
895 throw ExceptionHelper.ConnectionNotOpened("Prepare",(Connection != null) ? Connection.State.ToString() : "");
\r
898 if (IsCommandPrepared) {
\r
899 // maybe we have to prepare the command again
\r
900 bool hasNullParameters = false;
\r
901 for(int i = 0; (i < Parameters.Count) && !hasNullParameters; i++) {
\r
902 AbstractDbParameter parameter = (AbstractDbParameter)Parameters[i];
\r
903 if (IsNullParameter(parameter)) {
\r
904 // if we still have null parameters - have to prepare agail
\r
905 IsCommandPrepared = false;
\r
906 hasNullParameters = true;
\r
910 if (!NullParametersInPrepare && hasNullParameters) {
\r
911 // if we prepeared using null parameters and now there is no null parameters - need to prepare again
\r
912 IsCommandPrepared = false;
\r
916 if (!IsCommandPrepared) {
\r
918 _javaCommandText = PrepareCommandTextAndParameters();
\r
920 java.sql.Connection jdbcCon = _connection.JdbcConnection;
\r
922 // For SchemaOnly we just prepare statement (for future use in GetSchemaTable)
\r
923 if (Behavior == CommandBehavior.SchemaOnly) {
\r
924 if (CommandType == CommandType.StoredProcedure)
\r
925 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
927 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
931 if (CommandType == CommandType.StoredProcedure)
\r
932 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
934 int internalParametersCount = InternalParameters.Count;
\r
935 if ( internalParametersCount > 0) {
\r
936 bool hasOnlyInputParameters = true;
\r
937 for(int i=0; i < internalParametersCount; i++) {
\r
938 AbstractDbParameter internalParameter = (AbstractDbParameter)InternalParameters[i];
\r
939 if (IsNullParameter(internalParameter)) {
\r
940 NullParametersInPrepare = true;
\r
943 if ((internalParameter.Direction & ParameterDirection.Output) != 0){
\r
944 hasOnlyInputParameters = false;
\r
948 if (hasOnlyInputParameters) {
\r
949 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
952 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
956 if (_explicitPrepare) {
\r
957 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
960 _statement = jdbcCon.createStatement();
\r
964 IsCommandPrepared = true;
\r
968 protected void BindParameters(ArrayList parameters)
\r
970 for(int parameterIndex = 0; parameterIndex < parameters.Count; parameterIndex++) {
\r
971 AbstractDbParameter parameter = (AbstractDbParameter)parameters[parameterIndex];
\r
972 switch (parameter.Direction) {
\r
973 case ParameterDirection.Input :
\r
974 BindInputParameter(parameter,parameterIndex);
\r
976 case ParameterDirection.InputOutput:
\r
977 BindInputParameter(parameter,parameterIndex);
\r
978 BindOutputParameter(parameter,parameterIndex);
\r
980 case ParameterDirection.Output :
\r
981 BindOutputParameter(parameter,parameterIndex);
\r
983 case ParameterDirection.ReturnValue :
\r
984 BindOutputParameter(parameter,parameterIndex);
\r
990 protected virtual void BindInputParameter(AbstractDbParameter parameter, int parameterIndex)
\r
992 object value = parameter.ConvertedValue;
\r
993 // java parameters are 1 based, while .net are 0 based
\r
995 PreparedStatement preparedStatement = ((PreparedStatement)_statement);
\r
997 switch ((DbConvert.JavaSqlTypes)parameter.JdbcType) {
\r
998 case DbConvert.JavaSqlTypes.DATALINK:
\r
999 case DbConvert.JavaSqlTypes.DISTINCT:
\r
1000 case DbConvert.JavaSqlTypes.JAVA_OBJECT:
\r
1001 case DbConvert.JavaSqlTypes.OTHER:
\r
1002 case DbConvert.JavaSqlTypes.REF:
\r
1003 case DbConvert.JavaSqlTypes.STRUCT: {
\r
1004 preparedStatement.setObject(parameterIndex, value, (int)parameter.JdbcType);
\r
1009 if ((value is DBNull) || (value == null)) {
\r
1010 preparedStatement.setNull(parameterIndex, (int)((AbstractDbParameter)parameter).JdbcType);
\r
1012 else if (value is long) {
\r
1013 preparedStatement.setLong(parameterIndex, (long)value);
\r
1015 else if (value is byte[]) {
\r
1016 if (((byte[])value).Length <= 4000) {
\r
1017 preparedStatement.setBytes(parameterIndex, vmw.common.TypeUtils.ToSByteArray((byte[]) value));
\r
1020 InputStream iStream=new ByteArrayInputStream(vmw.common.TypeUtils.ToSByteArray((byte[]) value));
\r
1021 preparedStatement.setBinaryStream(parameterIndex,iStream,((byte[])value).Length);
\r
1024 else if (value is byte) {
\r
1025 preparedStatement.setByte(parameterIndex, (sbyte)(byte)value);
\r
1027 else if (value is char[]) {
\r
1028 Reader reader = new CharArrayReader((char[])value);
\r
1029 preparedStatement.setCharacterStream(parameterIndex,reader,((char[])value).Length);
\r
1031 else if (value is bool) {
\r
1032 preparedStatement.setBoolean(parameterIndex, (bool) value);
\r
1034 else if (value is char) {
\r
1035 preparedStatement.setString(parameterIndex, ((char)value).ToString());
\r
1037 else if (value is DateTime) {
\r
1038 switch ((DbConvert.JavaSqlTypes)parameter.JdbcType) {
\r
1040 case DbConvert.JavaSqlTypes.TIMESTAMP:
\r
1041 preparedStatement.setTimestamp(parameterIndex,DbConvert.ClrTicksToJavaTimestamp(((DateTime)value).Ticks));
\r
1043 case DbConvert.JavaSqlTypes.TIME:
\r
1044 preparedStatement.setTime(parameterIndex,DbConvert.ClrTicksToJavaTime(((DateTime)value).Ticks));
\r
1046 case DbConvert.JavaSqlTypes.DATE:
\r
1047 preparedStatement.setDate(parameterIndex,DbConvert.ClrTicksToJavaDate(((DateTime)value).Ticks));
\r
1051 else if (value is TimeSpan) {
\r
1052 if (parameter.JdbcType == (int)DbConvert.JavaSqlTypes.TIMESTAMP)
\r
1053 preparedStatement.setTimestamp(parameterIndex,DbConvert.ClrTicksToJavaTimestamp(((TimeSpan)value).Ticks));
\r
1055 preparedStatement.setTime(parameterIndex,DbConvert.ClrTicksToJavaTime(((TimeSpan)value).Ticks));
\r
1057 else if (value is Decimal) {
\r
1058 preparedStatement.setBigDecimal(parameterIndex, vmw.common.PrimitiveTypeUtils.DecimalToBigDecimal((Decimal) value));
\r
1060 else if (value is double) {
\r
1061 preparedStatement.setDouble(parameterIndex, (double)value);
\r
1063 else if (value is float) {
\r
1064 preparedStatement.setFloat(parameterIndex, (float)value);
\r
1066 else if (value is int) {
\r
1067 preparedStatement.setInt(parameterIndex, (int)value);
\r
1069 else if (value is string) {
\r
1070 //can not be done for inout params, due to Oracle problem with FIXED_CHAR out param fetching
\r
1071 if (parameter.Direction == ParameterDirection.Input &&
\r
1072 preparedStatement is Mainsoft.Data.Jdbc.Providers.IPreparedStatement &&
\r
1073 (DbConvert.JavaSqlTypes)parameter.JdbcType == DbConvert.JavaSqlTypes.CHAR) {
\r
1074 ((Mainsoft.Data.Jdbc.Providers.IPreparedStatement)preparedStatement)
\r
1075 .setChar(parameterIndex, (string)value);
\r
1078 preparedStatement.setString(parameterIndex, (string)value);
\r
1080 else if (value is Guid) {
\r
1081 preparedStatement.setString(parameterIndex, value.ToString());
\r
1083 else if (value is short) {
\r
1084 preparedStatement.setShort(parameterIndex, (short)value);
\r
1086 else if (value is sbyte) {
\r
1087 preparedStatement.setByte(parameterIndex, (sbyte)value);
\r
1090 preparedStatement.setObject(parameterIndex, value);
\r
1094 protected virtual void BindOutputParameter(AbstractDbParameter parameter, int parameterIndex)
1096 parameter.Validate();
1097 int jdbcType = (int)parameter.JdbcType;
\r
1098 // java parameters are 1 based, while .net are 0 based
\r
1101 CallableStatement callableStatement = ((CallableStatement)_statement);
\r
1103 // the scale has a meening only in DECIMAL and NUMERIC parameters
\r
1104 if (jdbcType == Types.DECIMAL || jdbcType == Types.NUMERIC) {
\r
1105 if(parameter.DbType == DbType.Currency) {
\r
1106 callableStatement.registerOutParameter(parameterIndex, jdbcType, 4);
\r
1109 callableStatement.registerOutParameter(parameterIndex, jdbcType, parameter.Scale);
\r
1113 callableStatement.registerOutParameter(parameterIndex, jdbcType);
\r
1117 private void FillOutputParameters()
1119 if (!(_statement is CallableStatement)) {
1122 for(int i = 0; i < InternalParameters.Count; i++) {
1123 AbstractDbParameter parameter = (AbstractDbParameter)InternalParameters[i];
1124 ParameterDirection direction = parameter.Direction;
1125 if (((direction & ParameterDirection.Output) != 0) && !SkipParameter(parameter)) {
1126 FillOutputParameter(parameter, i);
1128 // drop jdbc type of out parameter, since it possibly was updated in ExecuteReader
1129 parameter.IsJdbcTypeSet = false;
1133 protected virtual void FillOutputParameter(DbParameter parameter, int index)
\r
1135 CallableStatement callableStatement = (CallableStatement)_statement;
\r
1136 ParameterMetadataWrapper parameterMetadataWrapper = null;
\r
1137 // FIXME wait for other drivers to implement
\r
1139 // parameterMetadataWrapper = new ParameterMetadataWrapper(callableStatement.getParameterMetaData());
\r
1142 // // suppress error : ms driver for sql server does not implement getParameterMetaData
\r
1143 // // suppress exception : ms driver for sql server does not implement getParameterMetaData
\r
1145 DbConvert.JavaSqlTypes javaSqlType = (DbConvert.JavaSqlTypes)((AbstractDbParameter)parameter).JdbcType;
\r
1147 parameter.Value = DbConvert.JavaResultSetToClrWrapper(callableStatement,index,javaSqlType,parameter.Size,parameterMetadataWrapper);
\r
1149 catch(java.sql.SQLException e) {
\r
1150 throw CreateException(e);
\r
1154 // AbstractDbCommand acts as IEnumerator over JDBC statement
\r
1155 // AbstractDbCommand.NextResultSet corresponds to IEnumerator.MoveNext
\r
1156 protected internal virtual bool NextResultSet()
\r
1158 if (!_hasResultSet)
\r
1163 _hasResultSet = _statement.getMoreResults();
\r
1164 if (_hasResultSet)
\r
1166 int updateCount = _statement.getUpdateCount();
\r
1167 if (updateCount < 0)
\r
1170 AccumulateRecordsAffected(updateCount);
\r
1173 catch (SQLException e) {
\r
1174 throw CreateException(e);
\r
1177 _currentResultSet = null;
\r
1181 private void AccumulateRecordsAffected(int updateCount)
\r
1183 if (_recordsAffected < 0) {
\r
1184 _recordsAffected = updateCount;
\r
1187 _recordsAffected += updateCount;
\r
1191 internal void OnReaderClosed(object reader)
\r
1194 if (Connection != null) {
\r
1195 ((AbstractDBConnection)Connection).RemoveReference(reader);
\r
1196 ((AbstractDBConnection)Connection).IsFetching = false;
\r
1197 if ((Behavior & CommandBehavior.CloseConnection) != 0) {
\r
1198 Connection.Close();
\r
1203 internal void CloseInternal()
\r
1205 if (Behavior != CommandBehavior.SchemaOnly) {
\r
1206 if (_statement != null) {
\r
1207 while (NextResultSet()) {
\r
1209 FillOutputParameters();
\r
1212 _currentReader = null;
\r
1216 protected override void Dispose(bool disposing)
\r
1221 base.Dispose(disposing);
\r
1224 private void CleanUp()
\r
1226 if (_currentReader != null) {
\r
1227 // we must preserve statement object until we have an associated reader object that might access it.
\r
1230 if (Connection != null) {
\r
1231 ((AbstractDBConnection)Connection).RemoveReference(this);
\r
1233 if (_statement != null) {
\r
1234 _statement.close();
\r
1235 _statement = null;
\r
1237 IsCommandPrepared = false;
\r
1238 _internalParameters = null;
\r
1239 _currentResultSet = null;
\r
1242 internal void OnSchemaChanging()
\r
1246 #endregion // Methods
\r
1248 #region ICloneable Members
\r
1250 public virtual object Clone() {
\r
1251 AbstractDbCommand target = (AbstractDbCommand)MemberwiseClone();
\r
1252 target._statement = null;
\r
1253 target._isCommandPrepared = false;
\r
1254 target._internalParameters = null;
\r
1255 target._javaCommandText = null;
\r
1256 target._recordsAffected = -1;
\r
1257 target._currentResultSet = null;
\r
1258 target._currentReader = null;
\r
1259 target._nullParametersInPrepare = false;
\r
1260 target._hasResultSet = false;
\r
1261 target._explicitPrepare = false;
\r
1262 if (Parameters != null && Parameters.Count > 0) {
\r
1263 target._parameters = CreateParameterCollection(target);
\r
1264 for(int i=0 ; i < Parameters.Count; i++) {
\r
1265 target.Parameters.Add(((AbstractDbParameter)Parameters[i]).Clone());
\r