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 : DbCommandBase, 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
104 private DbParameterCollection _parameters;
\r
105 private java.sql.Statement _statement;
\r
106 private AbstractDBConnection _connection;
\r
107 private AbstractTransaction _transaction;
\r
108 private bool _isCommandPrepared;
\r
109 private CommandBehavior _behavior;
\r
110 private ArrayList _internalParameters;
\r
111 string _javaCommandText;
\r
112 private int _recordsAffected;
\r
113 private ResultSet _currentResultSet;
\r
114 private DbDataReader _currentReader;
\r
115 private bool _nullParametersInPrepare;
\r
116 private bool _hasResultSet;
\r
117 private bool _explicitPrepare;
\r
119 static ProcedureColumnCache _procedureColumnCache = new ProcedureColumnCache();
\r
121 #endregion // Fields
\r
123 #region Constructors
\r
125 public AbstractDbCommand(
\r
127 AbstractDBConnection connection,
\r
128 AbstractTransaction transaction)
\r
130 _connection = connection;
\r
131 base.CommandText = cmdText;
\r
132 _transaction = transaction;
\r
133 _isCommandPrepared = false;
\r
134 _explicitPrepare = false;
\r
135 _recordsAffected = -1;
\r
136 if (connection != null) {
\r
137 connection.AddReference(this);
\r
141 #endregion // Constructors
\r
145 protected override DbParameterCollection DbParameterCollection
\r
148 if (_parameters == null) {
\r
149 _parameters = CreateParameterCollection(this);
\r
151 return _parameters;
\r
155 protected override DbConnection DbConnection
\r
157 get { return (DbConnection)_connection; }
\r
159 if (value == _connection) {
\r
163 if (_currentReader != null && !_currentReader.IsClosed) {
\r
164 throw ExceptionHelper.ConnectionIsBusy(this.GetType().Name,((AbstractDBConnection)_connection).InternalState);
\r
166 if (_connection != null) {
\r
167 _connection.RemoveReference(this);
\r
169 _connection = (AbstractDBConnection) value;
\r
170 if (_connection != null) {
\r
171 _connection.AddReference(this);
\r
176 protected override DbTransaction DbTransaction
\r
178 get { return _transaction != null ? _transaction.ActiveTransaction : null; }
\r
179 set { _transaction = (AbstractTransaction)value; }
\r
182 public override string CommandText
\r
184 get { return base.CommandText; }
\r
186 if (CommandText == null || String.Compare(CommandText, value, true) != 0) {
\r
187 base.CommandText = value;
\r
188 _isCommandPrepared = false;
\r
189 _explicitPrepare = false;
\r
194 internal CommandBehavior Behavior
\r
196 get { return _behavior; }
\r
197 set { _behavior = value; }
\r
200 bool IsCommandPrepared
\r
202 get { return _isCommandPrepared; }
\r
203 set { _isCommandPrepared = value; }
\r
206 bool NullParametersInPrepare
\r
208 get { return _nullParametersInPrepare; }
\r
209 set { _nullParametersInPrepare = value; }
\r
212 protected ArrayList InternalParameters
\r
215 if (_internalParameters == null) {
\r
216 _internalParameters = new ArrayList();
\r
218 return _internalParameters;
\r
222 // Number of records affected by execution of batch statement
\r
223 // -1 for SELECT statements.
\r
224 internal int RecordsAffected
\r
227 return _recordsAffected;
\r
231 // AbstractDbCommand acts as IEnumerator over JDBC statement
\r
232 // AbstractDbCommand.CurrentResultSet corresponds to IEnumerator.Current
\r
233 protected internal virtual ResultSet CurrentResultSet
\r
237 if (_currentResultSet == null && _hasResultSet) {
\r
238 _currentResultSet = _statement.getResultSet();
\r
240 return _currentResultSet;
\r
242 catch(SQLException e) {
\r
243 throw new Exception(e.Message, e);
\r
248 protected internal java.sql.Statement Statement
\r
250 get { return _statement; }
\r
252 #if USE_DOTNET_REGEX
\r
253 protected virtual Regex StoredProcedureRegExp
\r
255 protected virtual Pattern StoredProcedureRegExp
\r
258 get { return SqlStatementsHelper.SimpleParameterStoredProcedureRegExp; }
\r
261 protected virtual SimpleRegex ParameterRegExp
\r
263 get { return SqlStatementsHelper.SimpleParameterRegExp; }
\r
266 #endregion // Properties
\r
270 protected abstract DbParameter CreateParameterInternal();
\r
272 protected abstract void CheckParameters();
\r
274 protected abstract DbDataReader CreateReader();
\r
276 protected abstract DbParameterCollection CreateParameterCollection(AbstractDbCommand parent);
\r
278 protected internal abstract SystemException CreateException(SQLException e);
\r
280 public override void Cancel()
\r
283 if (_statement != null)
\r
284 _statement.cancel();
\r
287 // MSDN says : "If there is nothing to cancel, nothing happens.
\r
288 // However, if there is a command in process, and the attempt to cancel fails,
\r
289 // no exception is generated."
\r
293 protected virtual bool SkipParameter(DbParameter parameter)
\r
298 protected sealed override DbParameter CreateDbParameter()
\r
300 return CreateParameterInternal();
\r
303 internal ArrayList DeriveParameters(string procedureName, bool throwIfNotExist)
\r
306 ArrayList col = _procedureColumnCache.GetProcedureColumns((AbstractDBConnection)Connection, procedureName, this);
\r
308 if (throwIfNotExist)
\r
309 throw ExceptionHelper.NoStoredProcedureExists(procedureName);
\r
310 col = new ArrayList();
\r
315 catch(SQLException e) {
\r
316 throw CreateException(e);
\r
320 string CreateTableDirectCommandText(string tableNames) {
\r
321 string forBrowse = String.Empty;
\r
322 if ((Behavior & CommandBehavior.KeyInfo) != 0) {
\r
323 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
324 if (connection != null) {
\r
325 string dbname = connection.JdbcConnection.getMetaData().getDatabaseProductName();
\r
326 if (dbname == "Microsoft SQL Server") //must add "FOR BROWSE" for selects
\r
327 forBrowse = " FOR BROWSE";
\r
331 string[] names = tableNames.Split(',');
\r
332 StringBuilder sb = new StringBuilder();
\r
334 for(int i = 0; i < names.Length; i++) {
\r
335 sb.Append("SELECT * FROM ");
\r
336 sb.Append(names[i]);
\r
337 sb.Append(forBrowse);
\r
341 if(names.Length <= 1) {
\r
342 sb.Remove(sb.Length - 1,1);
\r
344 return sb.ToString();
\r
347 private string PrepareCommandTextAndParameters()
\r
349 NullParametersInPrepare = false;
\r
350 switch (CommandType) {
\r
351 case CommandType.TableDirect :
\r
352 return CreateTableDirectCommandText(CommandText);
\r
353 case CommandType.StoredProcedure :
\r
354 return CreateStoredProcedureCommandTextSimple(CommandText, Parameters, DeriveParameters(CommandText, false));
\r
355 case CommandType.Text :
\r
357 int userParametersPosition = 0;
\r
358 int charsConsumed = 0;
\r
359 StringBuilder sb = new StringBuilder(CommandText.Length);
\r
361 for (SimpleMatch match = SqlStatementsHelper.CompoundStatementSplitterReqExp.Match(CommandText);
\r
363 match = match.NextMatch()) {
\r
365 int length = match.Length;
\r
370 int start = match.Index;
\r
371 string value = match.Value;
\r
373 sb.Append(CommandText, charsConsumed, start-charsConsumed);
\r
374 charsConsumed = start + length;
\r
376 #if USE_DOTNET_REGEX
\r
377 Match storedProcMatch = StoredProcedureRegExp.Match(value);
\r
378 // count parameters for all kinds of simple statements
\r
379 userParametersPosition +=
\r
380 (storedProcMatch.Success) ?
\r
381 // statement is stored procedure call
\r
382 CreateStoredProcedureCommandText(sb, value, storedProcMatch, Parameters, userParametersPosition) :
\r
383 // statement is a simple SQL query
\r
384 PrepareSimpleQuery(sb, value, Parameters, userParametersPosition);
\r
386 Matcher storedProcMatch = StoredProcedureRegExp.matcher((java.lang.CharSequence)(object)value);
\r
387 userParametersPosition +=
\r
388 (storedProcMatch.find()) ?
\r
389 // statement is stored procedure call
\r
390 CreateStoredProcedureCommandText(sb, value, storedProcMatch, Parameters, userParametersPosition) :
\r
391 // statement is a simple SQL query
\r
392 PrepareSimpleQuery(sb, value, Parameters, userParametersPosition);
\r
396 sb.Append(CommandText, charsConsumed, CommandText.Length-charsConsumed);
\r
398 return sb.ToString();
\r
403 string CreateStoredProcedureCommandTextSimple(string procedureName, IDataParameterCollection userParams, IList derivedParams) {
\r
404 StringBuilder sb = new StringBuilder();
\r
406 int curUserPos = 0;
\r
407 int curDerivedPos = 0;
\r
408 bool addParas = true;
\r
409 string trimedProcedureName = (procedureName != null) ? procedureName.TrimEnd() : String.Empty;
\r
410 if (trimedProcedureName.Length > 0 && trimedProcedureName[trimedProcedureName.Length-1] == ')')
\r
413 AbstractDbParameter derivedParam = (derivedParams.Count > 0) ? (AbstractDbParameter)derivedParams[curDerivedPos] : null;
\r
414 if (derivedParam != null) {
\r
415 if (derivedParam.Direction == ParameterDirection.ReturnValue)
\r
418 derivedParam = null; //play as if there is no retval parameter
\r
420 AbstractDbParameter returnValueParameter = GetReturnParameter (userParams);
\r
421 if (returnValueParameter != null) {
\r
423 InternalParameters.Add(returnValueParameter);
\r
424 sb.Append("{? = call ");
\r
426 if (derivedParam != null && !returnValueParameter.IsDbTypeSet) {
\r
427 returnValueParameter.JdbcType = derivedParam.JdbcType;
\r
431 sb.Append("{call ");
\r
434 sb.Append(procedureName);
\r
438 bool needComma = false;
\r
439 for (int i = curDerivedPos; i < derivedParams.Count; i++) {
\r
440 AbstractDbParameter derivedParameter = (AbstractDbParameter)derivedParams[curDerivedPos++];
\r
442 bool addParam = false;
\r
444 if (derivedParameter.IsSpecial) {
\r
445 // derived parameter is special - never appears in user parameters or user values
\r
446 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
450 AbstractDbParameter userParameter = GetUserParameter(derivedParameter.Placeholder, userParams, curUserPos);
\r
451 if (userParameter != null) {
\r
453 InternalParameters.Add(userParameter);
\r
456 if (derivedParameter != null && !userParameter.IsDbTypeSet) {
\r
457 userParameter.JdbcType = derivedParameter.JdbcType;
\r
472 for (int i = curUserPos; i < userParams.Count; i++) {
\r
478 AbstractDbParameter userParameter = (AbstractDbParameter)userParams[curUserPos++];
\r
479 InternalParameters.Add(userParameter);
\r
487 return sb.ToString();
\r
491 /// We suppose that user parameters are in the same order as devived parameters except the special cases
\r
492 /// (return value, oracle ref cursors etc.)
\r
494 //protected virtual string CreateStoredProcedureCommandText(string procedureName, IList userParametersList, int userParametersListStart/*, int userParametersListCount*/, string[] userValuesList, ArrayList derivedParametersList)
\r
495 #if USE_DOTNET_REGEX
\r
496 int CreateStoredProcedureCommandText(StringBuilder sb, string sql, Match match, IDataParameterCollection userParams, int userParamsStartPosition)
\r
498 int CreateStoredProcedureCommandText(StringBuilder sb, string sql, Matcher match, IDataParameterCollection userParams, int userParamsStartPosition)
\r
501 int curUserPos = userParamsStartPosition;
\r
502 #if USE_DOTNET_REGEX
\r
503 Group procNameGroup = null;
\r
505 for (Match procNameMatch = match; procNameMatch.Success; procNameMatch = procNameMatch.NextMatch()){
\r
506 procNameGroup = match.Groups["PROCNAME"];
\r
507 if (!procNameGroup.Success) {
\r
512 if (procNameGroup == null || !procNameGroup.Success)
\r
513 throw new ArgumentException("Not a stored procedure call: '{0}'", sql);
\r
515 ArrayList derivedParameters = DeriveParameters(procNameGroup.Value, false);
\r
517 ArrayList derivedParameters = DeriveParameters(match.group(2).Trim(), false);
\r
519 int curDerivedPos = 0;
\r
521 AbstractDbParameter retValderivedParameter = curDerivedPos < derivedParameters.Count ?
\r
522 (AbstractDbParameter)derivedParameters[curDerivedPos] : null;
\r
523 if (retValderivedParameter != null && retValderivedParameter.Direction == ParameterDirection.ReturnValue)
\r
526 int queryCurrentPosition = 0;
\r
528 #if USE_DOTNET_REGEX
\r
529 for (Match retValMatch = match; retValMatch.Success; retValMatch = retValMatch.NextMatch()){
\r
530 Group retval = retValMatch.Groups["RETVAL"];
\r
531 if (!retval.Success) {
\r
535 int retvalIndex = retval.Index;
\r
536 string retvalValue = retval.Value;
\r
537 int retvalLength = retval.Length;
\r
539 int retvalIndex = match.start(1);
\r
540 for (;retvalIndex >= 0;) {
\r
541 string retvalValue = match.group(1);
\r
542 int retvalLength = retvalValue.Length;
\r
545 sb.Append(sql, queryCurrentPosition, retvalIndex);
\r
546 AbstractDbParameter userParameter = GetUserParameter(retvalValue, userParams, curUserPos);
\r
547 if (userParameter != null) {
\r
549 InternalParameters.Add(userParameter);
\r
551 if (retValderivedParameter != null && !userParameter.IsDbTypeSet) {
\r
552 userParameter.JdbcType = retValderivedParameter.JdbcType;
\r
558 sb.Append(retvalValue);
\r
561 queryCurrentPosition = (retvalIndex + retvalLength);
\r
566 #if USE_DOTNET_REGEX
\r
567 sb.Append(sql, queryCurrentPosition, procNameGroup.Index + procNameGroup.Length - queryCurrentPosition);
\r
568 queryCurrentPosition = procNameGroup.Index + procNameGroup.Length;
\r
570 sb.Append(sql, queryCurrentPosition, match.end(2) - queryCurrentPosition);
\r
571 queryCurrentPosition = match.end(2);
\r
574 bool hasUserParams = false;
\r
576 #if USE_DOTNET_REGEX
\r
577 must rewrite the regex to not parse params to have single code with java regex
\r
579 int paramsStart = match.start(3);
\r
580 if (paramsStart >= 0) {
\r
583 hasUserParams = true;
\r
584 sb.Append(sql,queryCurrentPosition,paramsStart - queryCurrentPosition);
\r
585 queryCurrentPosition = paramsStart;
\r
587 for (SimpleMatch m = SqlStatementsHelper.ProcedureParameterSplitterReqExp.Match(match.group(3));
\r
588 m.Success;m = m.NextMatch()) {
\r
590 SimpleCapture parameterCapture = m;
\r
591 sb.Append(sql,queryCurrentPosition,paramsStart + parameterCapture.Index - queryCurrentPosition);
\r
593 // advance in query
\r
594 queryCurrentPosition = paramsStart + parameterCapture.Index + parameterCapture.Length;
\r
596 AbstractDbParameter derivedParameter = curDerivedPos < derivedParameters.Count ?
\r
597 (AbstractDbParameter)derivedParameters[curDerivedPos++] : null;
\r
599 //check for special params
\r
600 while (derivedParameter != null && derivedParameter.IsSpecial) {
\r
601 // derived parameter is special - never appears in user parameters or user values
\r
602 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
606 derivedParameter = curDerivedPos < derivedParameters.Count ?
\r
607 (AbstractDbParameter)derivedParameters[curDerivedPos++] : null;
\r
610 AbstractDbParameter userParameter = GetUserParameter(parameterCapture.Value.Trim(), userParams, curUserPos);
\r
612 if (userParameter != null) {
\r
614 InternalParameters.Add(userParameter);
\r
615 if (derivedParameter != null && !userParameter.IsDbTypeSet) {
\r
616 userParameter.JdbcType = derivedParameter.JdbcType;
\r
618 // advance in user parameters
\r
622 sb.Append(parameterCapture.Value);
\r
627 bool addedSpecialParams = false;
\r
629 for (int i = curDerivedPos; i < derivedParameters.Count;) {
\r
630 AbstractDbParameter derivedParameter = (AbstractDbParameter)derivedParameters[i++];
\r
631 if (derivedParameter.IsSpecial) {
\r
632 // derived parameter is special - never appears in user parameters or user values
\r
633 if (!hasUserParams && !addedSpecialParams) {
\r
634 addedSpecialParams = true;
\r
639 for (;curDerivedPos < i;curDerivedPos++)
\r
642 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
647 if (!hasUserParams && addedSpecialParams)
\r
650 sb.Append(sql,queryCurrentPosition,sql.Length - queryCurrentPosition);
\r
651 return curUserPos - userParamsStartPosition;
\r
654 protected virtual AbstractDbParameter GetUserParameter(string parameterName, IList userParametersList, int userParametersListPosition)
\r
656 if (userParametersListPosition < userParametersList.Count) {
\r
657 AbstractDbParameter param = (AbstractDbParameter)userParametersList[userParametersListPosition];
\r
658 if (param.Placeholder == parameterName)
\r
664 protected virtual AbstractDbParameter GetReturnParameter (IList userParametersList)
\r
666 AbstractDbParameter param = GetUserParameter ("?", userParametersList, 0);
\r
668 if (param != null && param.Direction == ParameterDirection.ReturnValue)
\r
674 int PrepareSimpleQuery(StringBuilder sb, string query, IList userParametersList, int userParametersListStart)
\r
676 int queryCurrentPosition = 0;
\r
677 int userParametersListPosition = userParametersListStart;
\r
679 if (userParametersList.Count > 0) {
\r
680 for (SimpleMatch m = ParameterRegExp.Match(query);
\r
681 m.Success;m = m.NextMatch()) {
\r
683 SimpleCapture parameterCapture = m;
\r
684 sb.Append(query,queryCurrentPosition,parameterCapture.Index - queryCurrentPosition);
\r
686 // advance in query
\r
687 queryCurrentPosition = parameterCapture.Index + parameterCapture.Length;
\r
689 AbstractDbParameter userParameter = GetUserParameter(parameterCapture.Value, userParametersList, userParametersListPosition);
\r
691 if (userParameter != null) {
\r
692 if (IsNullParameter(userParameter)) {
\r
694 NullParametersInPrepare = true;
\r
698 InternalParameters.Add(userParameter);
\r
700 // advance in user parameters
\r
701 userParametersListPosition++;
\r
704 sb.Append(parameterCapture.Value);
\r
709 sb.Append(query,queryCurrentPosition,query.Length - queryCurrentPosition);
\r
710 int userParamsConsumed = userParametersListPosition - userParametersListStart;
\r
712 if ((Behavior & CommandBehavior.KeyInfo) == 0)
\r
713 return userParamsConsumed;
\r
715 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
716 if (connection == null)
\r
717 return userParamsConsumed;
\r
719 string dbname = connection.JdbcConnection.getMetaData().getDatabaseProductName();
\r
720 if (dbname == "Microsoft SQL Server") { //must add "FOR BROWSE" for selects
\r
721 #if USE_DOTNET_REGEX
\r
722 if (!SqlStatementsHelper.ForBrowseStatementReqExp.IsMatch(query))
\r
723 sb.Append(" FOR BROWSE");
\r
725 if (!SqlStatementsHelper.ForBrowseStatementReqExp.matcher ((java.lang.CharSequence)(object)query).find ())
\r
726 sb.Append (" FOR BROWSE");
\r
730 return userParamsConsumed;
\r
733 protected virtual bool IsNullParameter(AbstractDbParameter parameter)
\r
735 return ((parameter.Value == null || parameter.Value == DBNull.Value) && !parameter.IsDbTypeSet);
\r
738 protected virtual void PrepareInternalParameters()
\r
740 InternalParameters.Clear();
\r
743 protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
\r
745 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
746 if (connection == null) {
\r
747 throw ExceptionHelper.ConnectionNotInitialized("ExecuteReader");
\r
750 IDbTransaction transaction = Transaction;
\r
751 if ((transaction != null && transaction.Connection != connection) ||
\r
752 (transaction == null && !connection.JdbcConnection.getAutoCommit())) {
\r
753 throw ExceptionHelper.TransactionNotInitialized();
\r
756 connection.IsExecuting = true;
\r
759 Behavior = behavior;
\r
761 PrepareInternalParameters();
\r
764 // For SchemaOnly there is no need for statement execution
\r
765 if (Behavior != CommandBehavior.SchemaOnly) {
\r
766 _recordsAffected = -1;
\r
768 // FIXME: this causes SP in MS Sql Server to create no mor than one row.
\r
769 if ((Behavior & CommandBehavior.SingleRow) != 0) {
\r
770 _statement.setFetchSize(1);
\r
773 if(_statement is PreparedStatement) {
\r
774 BindParameters(InternalParameters);
\r
775 _hasResultSet = ((PreparedStatement)_statement).execute();
\r
778 _hasResultSet =_statement.execute(_javaCommandText);
\r
781 if (!_hasResultSet) {
\r
782 int updateCount = _statement.getUpdateCount();
\r
783 if (updateCount >= 0) {
\r
784 AccumulateRecordsAffected(updateCount);
\r
785 _hasResultSet = true; //play as if we have resultset
\r
790 connection.IsFetching = true;
\r
792 _currentReader = CreateReader();
\r
794 catch(Exception e) {
\r
795 connection.IsFetching = false;
\r
798 return _currentReader;
\r
800 catch(SQLException e) {
\r
801 throw CreateException(e);
\r
804 connection.IsExecuting = false;
\r
805 NullParametersInPrepare = false;
\r
809 public override void Prepare()
\r
811 ((AbstractDBConnection)Connection).IsExecuting = true;
\r
814 _explicitPrepare = true;
\r
817 ((AbstractDBConnection)Connection).IsExecuting = false;
\r
821 private void PrepareInternal()
\r
823 if ((Connection == null) || (Connection.State != ConnectionState.Open)) {
\r
824 throw ExceptionHelper.ConnectionNotOpened("Prepare",(Connection != null) ? Connection.State.ToString() : "");
\r
827 if (IsCommandPrepared) {
\r
828 // maybe we have to prepare the command again
\r
829 bool hasNullParameters = false;
\r
830 for(int i = 0; (i < Parameters.Count) && !hasNullParameters; i++) {
\r
831 AbstractDbParameter parameter = (AbstractDbParameter)Parameters[i];
\r
832 if (IsNullParameter(parameter)) {
\r
833 // if we still have null parameters - have to prepare agail
\r
834 IsCommandPrepared = false;
\r
835 hasNullParameters = true;
\r
839 if (!NullParametersInPrepare && hasNullParameters) {
\r
840 // if we prepeared using null parameters and now there is no null parameters - need to prepare again
\r
841 IsCommandPrepared = false;
\r
845 if (!IsCommandPrepared) {
\r
847 _javaCommandText = PrepareCommandTextAndParameters();
\r
849 java.sql.Connection jdbcCon = _connection.JdbcConnection;
\r
851 // For SchemaOnly we just prepare statement (for future use in GetSchemaTable)
\r
852 if (Behavior == CommandBehavior.SchemaOnly) {
\r
853 if (CommandType == CommandType.StoredProcedure)
\r
854 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
856 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
860 if (CommandType == CommandType.StoredProcedure)
\r
861 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
863 int internalParametersCount = InternalParameters.Count;
\r
864 if ( internalParametersCount > 0) {
\r
865 bool hasOnlyInputParameters = true;
\r
866 for(int i=0; i < internalParametersCount; i++) {
\r
867 AbstractDbParameter internalParameter = (AbstractDbParameter)InternalParameters[i];
\r
868 if (IsNullParameter(internalParameter)) {
\r
869 NullParametersInPrepare = true;
\r
872 if ((internalParameter.Direction & ParameterDirection.Output) != 0){
\r
873 hasOnlyInputParameters = false;
\r
877 if (hasOnlyInputParameters) {
\r
878 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
881 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
885 if (_explicitPrepare) {
\r
886 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
889 _statement = jdbcCon.createStatement();
\r
893 IsCommandPrepared = true;
\r
897 protected void BindParameters(ArrayList parameters)
\r
899 for(int parameterIndex = 0; parameterIndex < parameters.Count; parameterIndex++) {
\r
900 AbstractDbParameter parameter = (AbstractDbParameter)parameters[parameterIndex];
\r
901 switch (parameter.Direction) {
\r
902 case ParameterDirection.Input :
\r
903 BindInputParameter(parameter,parameterIndex);
\r
905 case ParameterDirection.InputOutput:
\r
906 BindInputParameter(parameter,parameterIndex);
\r
907 BindOutputParameter(parameter,parameterIndex);
\r
909 case ParameterDirection.Output :
\r
910 BindOutputParameter(parameter,parameterIndex);
\r
912 case ParameterDirection.ReturnValue :
\r
913 BindOutputParameter(parameter,parameterIndex);
\r
919 protected virtual void BindInputParameter(AbstractDbParameter parameter, int parameterIndex)
\r
921 object value = parameter.ConvertedValue;
\r
922 // java parameters are 1 based, while .net are 0 based
\r
924 PreparedStatement preparedStatement = ((PreparedStatement)_statement);
\r
926 switch ((DbConvert.JavaSqlTypes)parameter.JdbcType) {
\r
927 case DbConvert.JavaSqlTypes.DATALINK:
\r
928 case DbConvert.JavaSqlTypes.DISTINCT:
\r
929 case DbConvert.JavaSqlTypes.JAVA_OBJECT:
\r
930 case DbConvert.JavaSqlTypes.OTHER:
\r
931 case DbConvert.JavaSqlTypes.REF:
\r
932 case DbConvert.JavaSqlTypes.STRUCT: {
\r
933 preparedStatement.setObject(parameterIndex, value, (int)parameter.JdbcType);
\r
938 if ((value is DBNull) || (value == null)) {
\r
939 preparedStatement.setNull(parameterIndex, (int)((AbstractDbParameter)parameter).JdbcType);
\r
941 else if (value is long) {
\r
942 preparedStatement.setLong(parameterIndex, (long)value);
\r
944 else if (value is byte[]) {
\r
945 if (((byte[])value).Length <= 4000) {
\r
946 preparedStatement.setBytes(parameterIndex, vmw.common.TypeUtils.ToSByteArray((byte[]) value));
\r
949 InputStream iStream=new ByteArrayInputStream(vmw.common.TypeUtils.ToSByteArray((byte[]) value));
\r
950 preparedStatement.setBinaryStream(parameterIndex,iStream,((byte[])value).Length);
\r
953 else if (value is byte) {
\r
954 preparedStatement.setByte(parameterIndex, (sbyte)(byte)value);
\r
956 else if (value is char[]) {
\r
957 Reader reader = new CharArrayReader((char[])value);
\r
958 preparedStatement.setCharacterStream(parameterIndex,reader,((char[])value).Length);
\r
960 else if (value is bool) {
\r
961 preparedStatement.setBoolean(parameterIndex, (bool) value);
\r
963 else if (value is char) {
\r
964 preparedStatement.setString(parameterIndex, ((char)value).ToString());
\r
966 else if (value is DateTime) {
\r
967 switch ((DbConvert.JavaSqlTypes)parameter.JdbcType) {
\r
969 case DbConvert.JavaSqlTypes.TIMESTAMP:
\r
970 preparedStatement.setTimestamp(parameterIndex,DbConvert.ClrTicksToJavaTimestamp(((DateTime)value).Ticks));
\r
972 case DbConvert.JavaSqlTypes.TIME:
\r
973 preparedStatement.setTime(parameterIndex,DbConvert.ClrTicksToJavaTime(((DateTime)value).Ticks));
\r
975 case DbConvert.JavaSqlTypes.DATE:
\r
976 preparedStatement.setDate(parameterIndex,DbConvert.ClrTicksToJavaDate(((DateTime)value).Ticks));
\r
980 else if (value is TimeSpan) {
\r
981 if (parameter.JdbcType == (int)DbConvert.JavaSqlTypes.TIMESTAMP)
\r
982 preparedStatement.setTimestamp(parameterIndex,DbConvert.ClrTicksToJavaTimestamp(((TimeSpan)value).Ticks));
\r
984 preparedStatement.setTime(parameterIndex,DbConvert.ClrTicksToJavaTime(((TimeSpan)value).Ticks));
\r
986 else if (value is Decimal) {
\r
987 preparedStatement.setBigDecimal(parameterIndex, vmw.common.PrimitiveTypeUtils.DecimalToBigDecimal((Decimal) value));
\r
989 else if (value is double) {
\r
990 preparedStatement.setDouble(parameterIndex, (double)value);
\r
992 else if (value is float) {
\r
993 preparedStatement.setFloat(parameterIndex, (float)value);
\r
995 else if (value is int) {
\r
996 preparedStatement.setInt(parameterIndex, (int)value);
\r
998 else if (value is string) {
\r
999 //can not be done for inout params, due to Oracle problem with FIXED_CHAR out param fetching
\r
1000 if (parameter.Direction == ParameterDirection.Input &&
\r
1001 preparedStatement is Mainsoft.Data.Jdbc.Providers.IPreparedStatement &&
\r
1002 (DbConvert.JavaSqlTypes)parameter.JdbcType == DbConvert.JavaSqlTypes.CHAR) {
\r
1003 ((Mainsoft.Data.Jdbc.Providers.IPreparedStatement)preparedStatement)
\r
1004 .setChar(parameterIndex, (string)value);
\r
1007 preparedStatement.setString(parameterIndex, (string)value);
\r
1009 else if (value is Guid) {
\r
1010 preparedStatement.setString(parameterIndex, value.ToString());
\r
1012 else if (value is short) {
\r
1013 preparedStatement.setShort(parameterIndex, (short)value);
\r
1015 else if (value is sbyte) {
\r
1016 preparedStatement.setByte(parameterIndex, (sbyte)value);
\r
1019 preparedStatement.setObject(parameterIndex, value);
\r
1023 protected virtual void BindOutputParameter(AbstractDbParameter parameter, int parameterIndex)
1025 parameter.Validate();
1026 int jdbcType = (int)parameter.JdbcType;
\r
1027 // java parameters are 1 based, while .net are 0 based
\r
1030 CallableStatement callableStatement = ((CallableStatement)_statement);
\r
1032 // the scale has a meening only in DECIMAL and NUMERIC parameters
\r
1033 if (jdbcType == Types.DECIMAL || jdbcType == Types.NUMERIC) {
\r
1034 if(parameter.DbType == DbType.Currency) {
\r
1035 callableStatement.registerOutParameter(parameterIndex, jdbcType, 4);
\r
1038 callableStatement.registerOutParameter(parameterIndex, jdbcType, parameter.Scale);
\r
1042 callableStatement.registerOutParameter(parameterIndex, jdbcType);
\r
1046 private void FillOutputParameters()
1048 if (!(_statement is CallableStatement)) {
1051 for(int i = 0; i < InternalParameters.Count; i++) {
1052 AbstractDbParameter parameter = (AbstractDbParameter)InternalParameters[i];
1053 ParameterDirection direction = parameter.Direction;
1054 if (((direction & ParameterDirection.Output) != 0) && !SkipParameter(parameter)) {
1055 FillOutputParameter(parameter, i);
1057 // drop jdbc type of out parameter, since it possibly was updated in ExecuteReader
1058 parameter.IsJdbcTypeSet = false;
1062 protected virtual void FillOutputParameter(DbParameter parameter, int index)
\r
1064 CallableStatement callableStatement = (CallableStatement)_statement;
\r
1065 ParameterMetadataWrapper parameterMetadataWrapper = null;
\r
1066 // FIXME wait for other drivers to implement
\r
1068 // parameterMetadataWrapper = new ParameterMetadataWrapper(callableStatement.getParameterMetaData());
\r
1071 // // suppress error : ms driver for sql server does not implement getParameterMetaData
\r
1072 // // suppress exception : ms driver for sql server does not implement getParameterMetaData
\r
1074 DbConvert.JavaSqlTypes javaSqlType = (DbConvert.JavaSqlTypes)((AbstractDbParameter)parameter).JdbcType;
\r
1076 parameter.Value = DbConvert.JavaResultSetToClrWrapper(callableStatement,index,javaSqlType,parameter.Size,parameterMetadataWrapper);
\r
1078 catch(java.sql.SQLException e) {
\r
1079 throw CreateException(e);
\r
1083 // AbstractDbCommand acts as IEnumerator over JDBC statement
\r
1084 // AbstractDbCommand.NextResultSet corresponds to IEnumerator.MoveNext
\r
1085 protected internal virtual bool NextResultSet()
\r
1087 if (!_hasResultSet)
\r
1092 _hasResultSet = _statement.getMoreResults();
\r
1093 if (_hasResultSet)
\r
1095 int updateCount = _statement.getUpdateCount();
\r
1096 if (updateCount < 0)
\r
1099 AccumulateRecordsAffected(updateCount);
\r
1102 catch (SQLException e) {
\r
1103 throw CreateException(e);
\r
1106 _currentResultSet = null;
\r
1110 private void AccumulateRecordsAffected(int updateCount)
\r
1112 if (_recordsAffected < 0) {
\r
1113 _recordsAffected = updateCount;
\r
1116 _recordsAffected += updateCount;
\r
1120 internal void OnReaderClosed(object reader)
\r
1123 if (Connection != null) {
\r
1124 ((AbstractDBConnection)Connection).RemoveReference(reader);
\r
1125 ((AbstractDBConnection)Connection).IsFetching = false;
\r
1126 if ((Behavior & CommandBehavior.CloseConnection) != 0) {
\r
1127 Connection.Close();
\r
1132 internal void CloseInternal()
\r
1134 if (Behavior != CommandBehavior.SchemaOnly) {
\r
1135 if (_statement != null) {
\r
1136 while (NextResultSet()) {
\r
1138 FillOutputParameters();
\r
1141 _currentReader = null;
\r
1145 protected override void Dispose(bool disposing)
\r
1150 base.Dispose(disposing);
\r
1153 private void CleanUp()
\r
1155 if (_currentReader != null) {
\r
1156 // we must preserve statement object until we have an associated reader object that might access it.
\r
1159 if (Connection != null) {
\r
1160 ((AbstractDBConnection)Connection).RemoveReference(this);
\r
1162 if (_statement != null) {
\r
1163 _statement.close();
\r
1164 _statement = null;
\r
1166 IsCommandPrepared = false;
\r
1167 _internalParameters = null;
\r
1168 _currentResultSet = null;
\r
1171 internal void OnSchemaChanging()
\r
1175 #endregion // Methods
\r
1177 #region ICloneable Members
\r
1179 public virtual object Clone() {
\r
1180 AbstractDbCommand target = (AbstractDbCommand)MemberwiseClone();
\r
1181 target._statement = null;
\r
1182 target._isCommandPrepared = false;
\r
1183 target._internalParameters = null;
\r
1184 target._javaCommandText = null;
\r
1185 target._recordsAffected = -1;
\r
1186 target._currentResultSet = null;
\r
1187 target._currentReader = null;
\r
1188 target._nullParametersInPrepare = false;
\r
1189 target._hasResultSet = false;
\r
1190 target._explicitPrepare = false;
\r
1191 if (Parameters != null && Parameters.Count > 0) {
\r
1192 target._parameters = CreateParameterCollection(target);
\r
1193 for(int i=0 ; i < Parameters.Count; i++) {
\r
1194 target.Parameters.Add(((AbstractDbParameter)Parameters[i]).Clone());
\r