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
\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
85 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
86 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
89 // internal static readonly Regex NamedParameterRegExp = new Regex(@"((?<USERPARAM>@\w+)|(\[[^\[\]]*\])|(""([^""]|(""""))*"")|('([^']|(''))*'))*", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
\r
90 // internal static readonly Regex SimpleParameterRegExp = new Regex(@"((?<USERPARAM>\?)|(\[[^\[\]]*\])|(""([^""]|(""""))*"")|('([^']|(''))*'))*", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
\r
91 internal static readonly SimpleRegex NamedParameterRegExp = new SqlParamsRegex();
\r
92 internal static readonly SimpleRegex SimpleParameterRegExp = new OleDbParamsRegex();
\r
94 internal static readonly Regex SelectFromStatementReqExp = new Regex(@"^\s*SELECT\s+(((\[[^\[\]]*\])|(""([^""]|(""""))*"")|('([^']|(''))*')|[^'""\[])*\s+)*FROM\s+", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
\r
95 internal static readonly Regex ForBrowseStatementReqExp = new Regex(@"\s+FOR\s+BROWSE\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
\r
97 internal static readonly SimpleRegex CompoundStatementSplitterReqExp = new CharacterSplitterRegex(';');
\r
98 internal static readonly SimpleRegex ProcedureParameterSplitterReqExp = new CharacterSplitterRegex(',');
\r
103 #endregion // SqlStatementsHelper
107 protected DbParameterCollection _parameters;
\r
108 protected java.sql.Statement _statement;
\r
109 protected AbstractDBConnection _connection;
\r
110 protected AbstractTransaction _transaction;
\r
111 private bool _isCommandPrepared;
\r
112 protected CommandBehavior _behavior;
\r
113 private ArrayList _internalParameters;
\r
114 string _javaCommandText;
\r
115 private int _recordsAffected;
\r
116 private ResultSet _currentResultSet;
\r
117 private DbDataReader _currentReader;
\r
118 private bool _nullParametersInPrepare;
\r
119 private bool _hasResultSet;
\r
120 private bool _explicitPrepare;
\r
122 internal static ProcedureColumnCache _procedureColumnCache = new ProcedureColumnCache();
\r
124 #endregion // Fields
\r
126 #region Constructors
\r
128 public AbstractDbCommand(
\r
130 AbstractDBConnection connection,
\r
131 AbstractTransaction transaction)
\r
133 _connection = connection;
\r
134 base.CommandText = cmdText;
\r
135 _transaction = transaction;
\r
136 _isCommandPrepared = false;
\r
137 _explicitPrepare = false;
\r
138 _recordsAffected = -1;
\r
139 if (connection != null) {
\r
140 connection.AddReference(this);
\r
144 #endregion // Constructors
\r
148 protected override DbParameterCollection DbParameterCollection
\r
151 if (_parameters == null) {
\r
152 _parameters = CreateParameterCollection(this);
\r
154 return _parameters;
\r
158 protected override DbConnection DbConnection
\r
160 get { return (DbConnection)_connection; }
\r
162 if (value == _connection) {
\r
166 if (_currentReader != null && !_currentReader.IsClosed) {
\r
167 throw ExceptionHelper.ConnectionIsBusy(this.GetType().Name,((AbstractDBConnection)_connection).InternalState);
\r
169 if (_connection != null) {
\r
170 _connection.RemoveReference(this);
\r
172 _connection = (AbstractDBConnection) value;
\r
173 if (_connection != null) {
\r
174 _connection.AddReference(this);
\r
179 protected override DbTransaction DbTransaction
\r
181 get { return _transaction != null ? _transaction.ActiveTransaction : null; }
\r
182 set { _transaction = (AbstractTransaction)value; }
\r
185 public override string CommandText
\r
187 get { return base.CommandText; }
\r
189 if (CommandText == null || String.Compare(CommandText, value, true) != 0) {
\r
190 base.CommandText = value;
\r
191 _isCommandPrepared = false;
\r
192 _explicitPrepare = false;
\r
197 internal CommandBehavior Behavior
\r
199 get { return _behavior; }
\r
200 set { _behavior = value; }
\r
203 bool IsCommandPrepared
\r
205 get { return _isCommandPrepared; }
\r
206 set { _isCommandPrepared = value; }
\r
209 bool NullParametersInPrepare
\r
211 get { return _nullParametersInPrepare; }
\r
212 set { _nullParametersInPrepare = value; }
\r
215 protected ArrayList InternalParameters
\r
218 if (_internalParameters == null) {
\r
219 _internalParameters = new ArrayList();
\r
221 return _internalParameters;
\r
225 // Number of records affected by execution of batch statement
\r
226 // -1 for SELECT statements.
\r
227 internal int RecordsAffected
\r
230 return _recordsAffected;
\r
234 // AbstractDbCommand acts as IEnumerator over JDBC statement
\r
235 // AbstractDbCommand.CurrentResultSet corresponds to IEnumerator.Current
\r
236 internal virtual ResultSet CurrentResultSet
\r
240 if (_currentResultSet == null && _hasResultSet) {
\r
241 _currentResultSet = _statement.getResultSet();
\r
243 return _currentResultSet;
\r
245 catch(SQLException e) {
\r
246 throw new Exception(e.Message, e);
\r
251 internal java.sql.Statement JdbcStatement
\r
253 get { return _statement; }
\r
255 #if USE_DOTNET_REGEX
\r
256 protected virtual Regex StoredProcedureRegExp
\r
258 protected virtual Pattern StoredProcedureRegExp
\r
261 get { return SqlStatementsHelper.SimpleParameterStoredProcedureRegExp; }
\r
264 protected virtual SimpleRegex ParameterRegExp
\r
266 get { return SqlStatementsHelper.SimpleParameterRegExp; }
\r
269 #endregion // Properties
\r
273 protected abstract DbParameter CreateParameterInternal();
\r
275 protected abstract void CheckParameters();
\r
277 protected abstract DbDataReader CreateReader();
\r
279 protected abstract DbParameterCollection CreateParameterCollection(AbstractDbCommand parent);
\r
281 protected abstract SystemException CreateException(SQLException e);
\r
283 protected internal void CopyTo(AbstractDbCommand target)
\r
285 target._behavior = _behavior;
\r
286 target.CommandText = CommandText;
\r
287 target.CommandTimeout = CommandTimeout;
\r
288 target.CommandType = CommandType;
\r
289 target._connection = _connection;
\r
290 target._transaction = _transaction;
\r
291 target.UpdatedRowSource = UpdatedRowSource;
\r
293 if (Parameters != null && Parameters.Count > 0) {
\r
294 target._parameters = CreateParameterCollection(target);
\r
295 for(int i=0 ; i < Parameters.Count; i++) {
\r
296 target.Parameters.Add(((AbstractDbParameter)Parameters[i]).Clone());
\r
301 public override void Cancel()
\r
304 if (_statement != null)
\r
305 _statement.cancel();
\r
308 // MSDN says : "If there is nothing to cancel, nothing happens.
\r
309 // However, if there is a command in process, and the attempt to cancel fails,
\r
310 // no exception is generated."
\r
314 protected virtual bool SkipParameter(DbParameter parameter)
\r
319 protected override DbParameter CreateDbParameter()
\r
321 return CreateParameterInternal();
\r
324 internal void DeriveParameters ()
\r
326 if(CommandType != CommandType.StoredProcedure) {
\r
327 throw ExceptionHelper.DeriveParametersNotSupported(this.GetType(),CommandType);
\r
330 ArrayList parameters = DeriveParameters(CommandText, true);
\r
331 Parameters.Clear();
\r
332 foreach (AbstractDbParameter param in parameters) {
\r
333 Parameters.Add(param.Clone());
\r
337 protected ArrayList DeriveParameters(string procedureName, bool throwIfNotExist)
\r
340 ArrayList col = _procedureColumnCache.GetProcedureColumns((AbstractDBConnection)Connection, procedureName, this);
\r
342 if (throwIfNotExist)
\r
343 throw ExceptionHelper.NoStoredProcedureExists(procedureName);
\r
344 col = new ArrayList();
\r
349 catch(SQLException e) {
\r
350 throw CreateException(e);
\r
354 string CreateTableDirectCommandText(string tableNames) {
\r
355 string forBrowse = String.Empty;
\r
356 if ((Behavior & CommandBehavior.KeyInfo) != 0) {
\r
357 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
358 if (connection != null) {
\r
359 string dbname = connection.JdbcConnection.getMetaData().getDatabaseProductName();
\r
360 if (dbname == "Microsoft SQL Server") //must add "FOR BROWSE" for selects
\r
361 forBrowse = " FOR BROWSE";
\r
365 string[] names = tableNames.Split(',');
\r
366 StringBuilder sb = new StringBuilder();
\r
368 for(int i = 0; i < names.Length; i++) {
\r
369 sb.Append("SELECT * FROM ");
\r
370 sb.Append(names[i]);
\r
371 sb.Append(forBrowse);
\r
375 if(names.Length <= 1) {
\r
376 sb.Remove(sb.Length - 1,1);
\r
378 return sb.ToString();
\r
381 private string PrepareCommandTextAndParameters()
\r
383 NullParametersInPrepare = false;
\r
384 switch (CommandType) {
\r
385 case CommandType.TableDirect :
\r
386 return CreateTableDirectCommandText(CommandText);
\r
387 case CommandType.StoredProcedure :
\r
388 return CreateStoredProcedureCommandTextSimple(CommandText, Parameters, DeriveParameters(CommandText, false));
\r
389 case CommandType.Text :
\r
391 int userParametersPosition = 0;
\r
392 int charsConsumed = 0;
\r
393 StringBuilder sb = new StringBuilder(CommandText.Length);
\r
395 for (SimpleMatch match = SqlStatementsHelper.CompoundStatementSplitterReqExp.Match(CommandText);
\r
397 match = match.NextMatch()) {
\r
399 int length = match.Length;
\r
404 int start = match.Index;
\r
405 string value = match.Value;
\r
407 sb.Append(CommandText, charsConsumed, start-charsConsumed);
\r
408 charsConsumed = start + length;
\r
410 #if USE_DOTNET_REGEX
\r
411 Match storedProcMatch = StoredProcedureRegExp.Match(value);
\r
412 // count parameters for all kinds of simple statements
\r
413 userParametersPosition +=
\r
414 (storedProcMatch.Success) ?
\r
415 // statement is stored procedure call
\r
416 CreateStoredProcedureCommandText(sb, value, storedProcMatch, Parameters, userParametersPosition) :
\r
417 // statement is a simple SQL query
\r
418 PrepareSimpleQuery(sb, value, Parameters, userParametersPosition);
\r
420 Matcher storedProcMatch = StoredProcedureRegExp.matcher((java.lang.CharSequence)(object)value);
\r
421 userParametersPosition +=
\r
422 (storedProcMatch.find()) ?
\r
423 // statement is stored procedure call
\r
424 CreateStoredProcedureCommandText(sb, value, storedProcMatch, Parameters, userParametersPosition) :
\r
425 // statement is a simple SQL query
\r
426 PrepareSimpleQuery(sb, value, Parameters, userParametersPosition);
\r
430 sb.Append(CommandText, charsConsumed, CommandText.Length-charsConsumed);
\r
432 return sb.ToString();
\r
437 string CreateStoredProcedureCommandTextSimple(string procedureName, IDataParameterCollection userParams, IList derivedParams) {
\r
438 StringBuilder sb = new StringBuilder();
\r
440 int curUserPos = 0;
\r
441 int curDerivedPos = 0;
\r
442 bool addParas = true;
\r
443 string trimedProcedureName = (procedureName != null) ? procedureName.TrimEnd() : String.Empty;
\r
444 if (trimedProcedureName.Length > 0 && trimedProcedureName[trimedProcedureName.Length-1] == ')')
\r
447 if (derivedParams.Count > 0 && ((AbstractDbParameter)derivedParams[curDerivedPos]).Direction == ParameterDirection.ReturnValue) {
\r
448 AbstractDbParameter derivedParam = (AbstractDbParameter)derivedParams[curDerivedPos++];
\r
450 AbstractDbParameter userParameter = GetUserParameter(derivedParam.Placeholder, userParams, curUserPos);
\r
451 if (userParameter != null && userParameter.Direction == ParameterDirection.ReturnValue) {
\r
453 InternalParameters.Add(userParameter);
\r
454 sb.Append("{? = call ");
\r
456 if (derivedParam != null && !userParameter.IsDbTypeSet) {
\r
457 userParameter.JdbcType = derivedParam.JdbcType;
\r
461 sb.Append("{call ");
\r
465 if (userParams.Count > 0 &&
\r
466 ((AbstractDbParameter)userParams[0]).Direction == ParameterDirection.ReturnValue) {
\r
468 InternalParameters.Add(userParams[0]);
\r
469 sb.Append("{? = call ");
\r
472 sb.Append("{call ");
\r
475 sb.Append(procedureName);
\r
479 bool needComma = false;
\r
480 for (int i = curDerivedPos; i < derivedParams.Count; i++) {
\r
481 AbstractDbParameter derivedParameter = (AbstractDbParameter)derivedParams[curDerivedPos++];
\r
483 bool addParam = false;
\r
485 if (derivedParameter.IsSpecial) {
\r
486 // derived parameter is special - never appears in user parameters or user values
\r
487 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
491 AbstractDbParameter userParameter = GetUserParameter(derivedParameter.Placeholder, userParams, curUserPos);
\r
492 if (userParameter != null) {
\r
494 InternalParameters.Add(userParameter);
\r
497 if (derivedParameter != null && !userParameter.IsDbTypeSet) {
\r
498 userParameter.JdbcType = derivedParameter.JdbcType;
\r
513 for (int i = curUserPos; i < userParams.Count; i++) {
\r
519 AbstractDbParameter userParameter = (AbstractDbParameter)userParams[curUserPos++];
\r
520 InternalParameters.Add(userParameter);
\r
528 return sb.ToString();
\r
532 /// We suppose that user parameters are in the same order as devived parameters except the special cases
\r
533 /// (return value, oracle ref cursors etc.)
\r
535 //protected virtual string CreateStoredProcedureCommandText(string procedureName, IList userParametersList, int userParametersListStart/*, int userParametersListCount*/, string[] userValuesList, ArrayList derivedParametersList)
\r
536 #if USE_DOTNET_REGEX
\r
537 int CreateStoredProcedureCommandText(StringBuilder sb, string sql, Match match, IDataParameterCollection userParams, int userParamsStartPosition)
\r
539 int CreateStoredProcedureCommandText(StringBuilder sb, string sql, Matcher match, IDataParameterCollection userParams, int userParamsStartPosition)
\r
542 int curUserPos = userParamsStartPosition;
\r
543 #if USE_DOTNET_REGEX
\r
544 Group procNameGroup = null;
\r
546 for (Match procNameMatch = match; procNameMatch.Success; procNameMatch = procNameMatch.NextMatch()){
\r
547 procNameGroup = match.Groups["PROCNAME"];
\r
548 if (!procNameGroup.Success) {
\r
553 if (procNameGroup == null || !procNameGroup.Success)
\r
554 throw new ArgumentException("Not a stored procedure call: '{0}'", sql);
\r
556 ArrayList derivedParameters = DeriveParameters(procNameGroup.Value, false);
\r
558 ArrayList derivedParameters = DeriveParameters(match.group(2).Trim(), false);
\r
560 int curDerivedPos = 0;
\r
562 AbstractDbParameter retValderivedParameter = curDerivedPos < derivedParameters.Count ?
\r
563 (AbstractDbParameter)derivedParameters[curDerivedPos] : null;
\r
564 if (retValderivedParameter != null && retValderivedParameter.Direction == ParameterDirection.ReturnValue)
\r
567 int queryCurrentPosition = 0;
\r
569 #if USE_DOTNET_REGEX
\r
570 for (Match retValMatch = match; retValMatch.Success; retValMatch = retValMatch.NextMatch()){
\r
571 Group retval = retValMatch.Groups["RETVAL"];
\r
572 if (!retval.Success) {
\r
576 int retvalIndex = retval.Index;
\r
577 string retvalValue = retval.Value;
\r
578 int retvalLength = retval.Length;
\r
580 int retvalIndex = match.start(1);
\r
581 for (;retvalIndex >= 0;) {
\r
582 string retvalValue = match.group(1);
\r
583 int retvalLength = retvalValue.Length;
\r
586 sb.Append(sql, queryCurrentPosition, retvalIndex);
\r
587 AbstractDbParameter userParameter = GetUserParameter(retvalValue, userParams, curUserPos);
\r
588 if (userParameter != null) {
\r
590 InternalParameters.Add(userParameter);
\r
592 if (retValderivedParameter != null && !userParameter.IsDbTypeSet) {
\r
593 userParameter.JdbcType = retValderivedParameter.JdbcType;
\r
599 sb.Append(retvalValue);
\r
602 queryCurrentPosition = (retvalIndex + retvalLength);
\r
607 #if USE_DOTNET_REGEX
\r
608 sb.Append(sql, queryCurrentPosition, procNameGroup.Index + procNameGroup.Length - queryCurrentPosition);
\r
609 queryCurrentPosition = procNameGroup.Index + procNameGroup.Length;
\r
611 sb.Append(sql, queryCurrentPosition, match.end(2) - queryCurrentPosition);
\r
612 queryCurrentPosition = match.end(2);
\r
615 bool hasUserParams = false;
\r
617 #if USE_DOTNET_REGEX
\r
618 must rewrite the regex to not parse params to have single code with java regex
\r
620 int paramsStart = match.start(3);
\r
621 if (paramsStart >= 0) {
\r
624 hasUserParams = true;
\r
625 sb.Append(sql,queryCurrentPosition,paramsStart - queryCurrentPosition);
\r
626 queryCurrentPosition = paramsStart;
\r
628 for (SimpleMatch m = SqlStatementsHelper.ProcedureParameterSplitterReqExp.Match(match.group(3));
\r
629 m.Success;m = m.NextMatch()) {
\r
631 SimpleCapture parameterCapture = m;
\r
632 sb.Append(sql,queryCurrentPosition,paramsStart + parameterCapture.Index - queryCurrentPosition);
\r
634 // advance in query
\r
635 queryCurrentPosition = paramsStart + parameterCapture.Index + parameterCapture.Length;
\r
637 AbstractDbParameter derivedParameter = curDerivedPos < derivedParameters.Count ?
\r
638 (AbstractDbParameter)derivedParameters[curDerivedPos++] : null;
\r
640 //check for special params
\r
641 while (derivedParameter != null && derivedParameter.IsSpecial) {
\r
642 // derived parameter is special - never appears in user parameters or user values
\r
643 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
647 derivedParameter = curDerivedPos < derivedParameters.Count ?
\r
648 (AbstractDbParameter)derivedParameters[curDerivedPos++] : null;
\r
651 AbstractDbParameter userParameter = GetUserParameter(parameterCapture.Value.Trim(), userParams, curUserPos);
\r
653 if (userParameter != null) {
\r
655 InternalParameters.Add(userParameter);
\r
656 if (derivedParameter != null && !userParameter.IsDbTypeSet) {
\r
657 userParameter.JdbcType = derivedParameter.JdbcType;
\r
659 // advance in user parameters
\r
663 sb.Append(parameterCapture.Value);
\r
668 bool addedSpecialParams = false;
\r
670 for (int i = curDerivedPos; i < derivedParameters.Count;) {
\r
671 AbstractDbParameter derivedParameter = (AbstractDbParameter)derivedParameters[i++];
\r
672 if (derivedParameter.IsSpecial) {
\r
673 // derived parameter is special - never appears in user parameters or user values
\r
674 if (!hasUserParams && !addedSpecialParams) {
\r
675 addedSpecialParams = true;
\r
680 for (;curDerivedPos < i;curDerivedPos++)
\r
683 InternalParameters.Add((AbstractDbParameter)derivedParameter.Clone());
\r
688 if (!hasUserParams && addedSpecialParams)
\r
691 sb.Append(sql,queryCurrentPosition,sql.Length - queryCurrentPosition);
\r
692 return curUserPos - userParamsStartPosition;
\r
695 protected virtual AbstractDbParameter GetUserParameter(string parameterName, IList userParametersList, int userParametersListPosition)
\r
697 if (userParametersListPosition < userParametersList.Count) {
\r
698 AbstractDbParameter param = (AbstractDbParameter)userParametersList[userParametersListPosition];
\r
699 if (param.Placeholder == parameterName)
\r
705 int PrepareSimpleQuery(StringBuilder sb, string query, IList userParametersList, int userParametersListStart)
\r
707 int queryCurrentPosition = 0;
\r
708 int userParametersListPosition = userParametersListStart;
\r
710 if (userParametersList.Count > 0) {
\r
711 for (SimpleMatch m = ParameterRegExp.Match(query);
\r
712 m.Success;m = m.NextMatch()) {
\r
714 SimpleCapture parameterCapture = m;
\r
715 sb.Append(query,queryCurrentPosition,parameterCapture.Index - queryCurrentPosition);
\r
717 // advance in query
\r
718 queryCurrentPosition = parameterCapture.Index + parameterCapture.Length;
\r
720 AbstractDbParameter userParameter = GetUserParameter(parameterCapture.Value, userParametersList, userParametersListPosition);
\r
722 if (userParameter != null) {
\r
723 if (IsNullParameter(userParameter)) {
\r
725 NullParametersInPrepare = true;
\r
729 InternalParameters.Add(userParameter);
\r
731 // advance in user parameters
\r
732 userParametersListPosition++;
\r
735 sb.Append(parameterCapture.Value);
\r
740 sb.Append(query,queryCurrentPosition,query.Length - queryCurrentPosition);
\r
741 int userParamsConsumed = userParametersListPosition - userParametersListStart;
\r
743 if ((Behavior & CommandBehavior.KeyInfo) == 0)
\r
744 return userParamsConsumed;
\r
746 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
747 if (connection == null)
\r
748 return userParamsConsumed;
\r
750 string dbname = connection.JdbcConnection.getMetaData().getDatabaseProductName();
\r
751 if (dbname == "Microsoft SQL Server") { //must add "FOR BROWSE" for selects
\r
752 if (SqlStatementsHelper.SelectFromStatementReqExp.IsMatch(query))
\r
753 if (!SqlStatementsHelper.ForBrowseStatementReqExp.IsMatch(query))
\r
754 sb.Append(" FOR BROWSE");
\r
757 return userParamsConsumed;
\r
760 protected virtual bool IsNullParameter(AbstractDbParameter parameter)
\r
762 return ((parameter.Value == null || parameter.Value == DBNull.Value) && !parameter.IsDbTypeSet);
\r
765 protected virtual void PrepareInternalParameters()
\r
767 InternalParameters.Clear();
\r
770 protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
\r
772 AbstractDBConnection connection = (AbstractDBConnection)Connection;
\r
773 if (connection == null) {
\r
774 throw ExceptionHelper.ConnectionNotInitialized("ExecuteReader");
\r
777 IDbTransaction transaction = Transaction;
\r
778 if ((transaction != null && transaction.Connection != connection) ||
\r
779 (transaction == null && !connection.JdbcConnection.getAutoCommit())) {
\r
780 throw ExceptionHelper.TransactionNotInitialized();
\r
783 connection.IsExecuting = true;
\r
786 Behavior = behavior;
\r
788 PrepareInternalParameters();
\r
791 // For SchemaOnly there is no need for statement execution
\r
792 if (Behavior != CommandBehavior.SchemaOnly) {
\r
793 _recordsAffected = -1;
\r
795 if ((Behavior & CommandBehavior.SingleRow) != 0) {
\r
796 _statement.setMaxRows(1);
\r
799 if(_statement is PreparedStatement) {
\r
800 BindParameters(InternalParameters);
\r
801 _hasResultSet = ((PreparedStatement)_statement).execute();
\r
804 _hasResultSet =_statement.execute(_javaCommandText);
\r
807 if (!_hasResultSet) {
\r
808 int updateCount = _statement.getUpdateCount();
\r
809 if (updateCount >= 0) {
\r
810 AccumulateRecordsAffected(updateCount);
\r
811 _hasResultSet = true; //play as if we have resultset
\r
816 connection.IsFetching = true;
\r
818 _currentReader = CreateReader();
\r
820 catch(Exception e) {
\r
821 connection.IsFetching = false;
\r
824 return _currentReader;
\r
826 catch(SQLException e) {
\r
827 throw CreateException(e);
\r
830 connection.IsExecuting = false;
\r
831 NullParametersInPrepare = false;
\r
835 public override void Prepare()
\r
837 ((AbstractDBConnection)Connection).IsExecuting = true;
\r
840 _explicitPrepare = true;
\r
843 ((AbstractDBConnection)Connection).IsExecuting = false;
\r
847 private void PrepareInternal()
\r
849 if ((Connection == null) || (Connection.State != ConnectionState.Open)) {
\r
850 throw ExceptionHelper.ConnectionNotOpened("Prepare",(Connection != null) ? Connection.State.ToString() : "");
\r
853 if (IsCommandPrepared) {
\r
854 // maybe we have to prepare the command again
\r
855 bool hasNullParameters = false;
\r
856 for(int i = 0; (i < Parameters.Count) && !hasNullParameters; i++) {
\r
857 AbstractDbParameter parameter = (AbstractDbParameter)Parameters[i];
\r
858 if (IsNullParameter(parameter)) {
\r
859 // if we still have null parameters - have to prepare agail
\r
860 IsCommandPrepared = false;
\r
861 hasNullParameters = true;
\r
865 if (!NullParametersInPrepare && hasNullParameters) {
\r
866 // if we prepeared using null parameters and now there is no null parameters - need to prepare again
\r
867 IsCommandPrepared = false;
\r
871 if (!IsCommandPrepared) {
\r
873 _javaCommandText = PrepareCommandTextAndParameters();
\r
875 java.sql.Connection jdbcCon = _connection.JdbcConnection;
\r
877 // For SchemaOnly we just prepare statement (for future use in GetSchemaTable)
\r
878 if (Behavior == CommandBehavior.SchemaOnly) {
\r
879 if (CommandType == CommandType.StoredProcedure)
\r
880 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
882 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
886 if (CommandType == CommandType.StoredProcedure)
\r
887 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
889 int internalParametersCount = InternalParameters.Count;
\r
890 if ( internalParametersCount > 0) {
\r
891 bool hasOnlyInputParameters = true;
\r
892 for(int i=0; i < internalParametersCount; i++) {
\r
893 AbstractDbParameter internalParameter = (AbstractDbParameter)InternalParameters[i];
\r
894 if (IsNullParameter(internalParameter)) {
\r
895 NullParametersInPrepare = true;
\r
898 if ((internalParameter.Direction & ParameterDirection.Output) != 0){
\r
899 hasOnlyInputParameters = false;
\r
903 if (hasOnlyInputParameters) {
\r
904 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
907 _statement = jdbcCon.prepareCall(_javaCommandText);
\r
911 if (_explicitPrepare) {
\r
912 _statement = jdbcCon.prepareStatement(_javaCommandText);
\r
915 _statement = jdbcCon.createStatement();
\r
919 IsCommandPrepared = true;
\r
923 protected void BindParameters(ArrayList parameters)
\r
925 for(int parameterIndex = 0; parameterIndex < parameters.Count; parameterIndex++) {
\r
926 AbstractDbParameter parameter = (AbstractDbParameter)parameters[parameterIndex];
\r
927 switch (parameter.Direction) {
\r
928 case ParameterDirection.Input :
\r
929 BindInputParameter(parameter,parameterIndex);
\r
931 case ParameterDirection.InputOutput:
\r
932 BindInputParameter(parameter,parameterIndex);
\r
933 BindOutputParameter(parameter,parameterIndex);
\r
935 case ParameterDirection.Output :
\r
936 BindOutputParameter(parameter,parameterIndex);
\r
938 case ParameterDirection.ReturnValue :
\r
939 BindOutputParameter(parameter,parameterIndex);
\r
945 protected virtual void BindInputParameter(AbstractDbParameter parameter, int parameterIndex)
\r
947 object value = parameter.ConvertedValue;
\r
948 // java parameters are 1 based, while .net are 0 based
\r
950 PreparedStatement preparedStatement = ((PreparedStatement)_statement);
\r
952 switch (parameter.JdbcType) {
\r
953 case DbTypes.JavaSqlTypes.DATALINK:
\r
954 case DbTypes.JavaSqlTypes.DISTINCT:
\r
955 case DbTypes.JavaSqlTypes.JAVA_OBJECT:
\r
956 case DbTypes.JavaSqlTypes.OTHER:
\r
957 case DbTypes.JavaSqlTypes.REF:
\r
958 case DbTypes.JavaSqlTypes.STRUCT: {
\r
959 preparedStatement.setObject(parameterIndex, value, (int)parameter.JdbcType);
\r
964 if ((value is DBNull) || (value == null)) {
\r
965 preparedStatement.setNull(parameterIndex, (int)((AbstractDbParameter)parameter).JdbcType);
\r
967 else if (value is long) {
\r
968 preparedStatement.setLong(parameterIndex, (long)value);
\r
970 else if (value is byte[]) {
\r
971 if (((byte[])value).Length <= 4000) {
\r
972 preparedStatement.setBytes(parameterIndex, vmw.common.TypeUtils.ToSByteArray((byte[]) value));
\r
975 InputStream iStream=new ByteArrayInputStream(vmw.common.TypeUtils.ToSByteArray((byte[]) value));
\r
976 preparedStatement.setBinaryStream(parameterIndex,iStream,((byte[])value).Length);
\r
979 else if (value is byte) {
\r
980 preparedStatement.setByte(parameterIndex, (sbyte)(byte)value);
\r
982 else if (value is char[]) {
\r
983 Reader reader = new CharArrayReader((char[])value);
\r
984 preparedStatement.setCharacterStream(parameterIndex,reader,((char[])value).Length);
\r
986 else if (value is bool) {
\r
987 preparedStatement.setBoolean(parameterIndex, (bool) value);
\r
989 else if (value is char) {
\r
990 preparedStatement.setString(parameterIndex, ((char)value).ToString());
\r
992 else if (value is DateTime) {
\r
993 switch (parameter.JdbcType) {
\r
995 case DbTypes.JavaSqlTypes.TIMESTAMP:
\r
996 preparedStatement.setTimestamp(parameterIndex,DbConvert.ClrTicksToJavaTimestamp(((DateTime)value).Ticks));
\r
998 case DbTypes.JavaSqlTypes.TIME:
\r
999 preparedStatement.setTime(parameterIndex,DbConvert.ClrTicksToJavaTime(((DateTime)value).Ticks));
\r
1001 case DbTypes.JavaSqlTypes.DATE:
\r
1002 preparedStatement.setDate(parameterIndex,DbConvert.ClrTicksToJavaDate(((DateTime)value).Ticks));
\r
1006 else if (value is TimeSpan) {
\r
1007 if (parameter.JdbcType == DbTypes.JavaSqlTypes.TIMESTAMP)
\r
1008 preparedStatement.setTimestamp(parameterIndex,DbConvert.ClrTicksToJavaTimestamp(((TimeSpan)value).Ticks));
\r
1010 preparedStatement.setTime(parameterIndex,DbConvert.ClrTicksToJavaTime(((TimeSpan)value).Ticks));
\r
1012 else if (value is Decimal) {
\r
1013 preparedStatement.setBigDecimal(parameterIndex, vmw.common.PrimitiveTypeUtils.DecimalToBigDecimal((Decimal) value));
\r
1015 else if (value is double) {
\r
1016 preparedStatement.setDouble(parameterIndex, (double)value);
\r
1018 else if (value is float) {
\r
1019 preparedStatement.setFloat(parameterIndex, (float)value);
\r
1021 else if (value is int) {
\r
1022 preparedStatement.setInt(parameterIndex, (int)value);
\r
1024 else if (value is string) {
\r
1025 preparedStatement.setString(parameterIndex, (string)value);
\r
1027 else if (value is Guid) {
\r
1028 preparedStatement.setString(parameterIndex, value.ToString());
\r
1030 else if (value is short) {
\r
1031 preparedStatement.setShort(parameterIndex, (short)value);
\r
1033 else if (value is sbyte) {
\r
1034 preparedStatement.setByte(parameterIndex, (sbyte)value);
\r
1037 preparedStatement.setObject(parameterIndex, value);
\r
1041 protected virtual void BindOutputParameter(AbstractDbParameter parameter, int parameterIndex)
1043 parameter.Validate();
1044 int jdbcType = (int)parameter.JdbcType;
\r
1045 // java parameters are 1 based, while .net are 0 based
\r
1048 CallableStatement callableStatement = ((CallableStatement)_statement);
\r
1050 // the scale has a meening only in DECIMAL and NUMERIC parameters
\r
1051 if (jdbcType == Types.DECIMAL || jdbcType == Types.NUMERIC) {
\r
1052 if(parameter.DbType == DbType.Currency) {
\r
1053 callableStatement.registerOutParameter(parameterIndex, jdbcType, 4);
\r
1056 callableStatement.registerOutParameter(parameterIndex, jdbcType, parameter.Scale);
\r
1060 callableStatement.registerOutParameter(parameterIndex, jdbcType);
\r
1064 private void FillOutputParameters()
1066 if (!(_statement is CallableStatement)) {
1069 for(int i = 0; i < InternalParameters.Count; i++) {
1070 AbstractDbParameter parameter = (AbstractDbParameter)InternalParameters[i];
1071 ParameterDirection direction = parameter.Direction;
1072 if (((direction & ParameterDirection.Output) != 0) && !SkipParameter(parameter)) {
1073 FillOutputParameter(parameter, i);
1075 // drop jdbc type of out parameter, since it possibly was updated in ExecuteReader
1076 parameter.IsJdbcTypeSet = false;
1080 protected virtual void FillOutputParameter(DbParameter parameter, int index)
\r
1082 CallableStatement callableStatement = (CallableStatement)_statement;
\r
1083 ParameterMetadataWrapper parameterMetadataWrapper = null;
\r
1084 // FIXME wait for other drivers to implement
\r
1086 // parameterMetadataWrapper = new ParameterMetadataWrapper(callableStatement.getParameterMetaData());
\r
1089 // // suppress error : ms driver for sql server does not implement getParameterMetaData
\r
1090 // // suppress exception : ms driver for sql server does not implement getParameterMetaData
\r
1092 DbTypes.JavaSqlTypes javaSqlType = (DbTypes.JavaSqlTypes)((AbstractDbParameter)parameter).JdbcType;
\r
1094 parameter.Value = DbConvert.JavaResultSetToClrWrapper(callableStatement,index,javaSqlType,parameter.Size,parameterMetadataWrapper);
\r
1096 catch(java.sql.SQLException e) {
\r
1097 throw CreateException(e);
\r
1101 // AbstractDbCommand acts as IEnumerator over JDBC statement
\r
1102 // AbstractDbCommand.NextResultSet corresponds to IEnumerator.MoveNext
\r
1103 internal virtual bool NextResultSet()
\r
1105 if (!_hasResultSet)
\r
1110 _hasResultSet = _statement.getMoreResults();
\r
1111 if (_hasResultSet)
\r
1113 int updateCount = _statement.getUpdateCount();
\r
1114 if (updateCount < 0)
\r
1117 AccumulateRecordsAffected(updateCount);
\r
1120 catch (SQLException e) {
\r
1121 throw CreateException(e);
\r
1124 _currentResultSet = null;
\r
1128 private void AccumulateRecordsAffected(int updateCount)
\r
1130 if (_recordsAffected < 0) {
\r
1131 _recordsAffected = updateCount;
\r
1134 _recordsAffected += updateCount;
\r
1138 internal void OnReaderClosed(object reader)
\r
1141 if (Connection != null) {
\r
1142 ((AbstractDBConnection)Connection).RemoveReference(reader);
\r
1143 ((AbstractDBConnection)Connection).IsFetching = false;
\r
1144 if ((Behavior & CommandBehavior.CloseConnection) != 0) {
\r
1145 Connection.Close();
\r
1150 internal void CloseInternal()
\r
1152 if (Behavior != CommandBehavior.SchemaOnly) {
\r
1153 if (_statement != null) {
\r
1154 while (NextResultSet()) {
\r
1156 FillOutputParameters();
\r
1159 _currentReader = null;
\r
1163 protected override void Dispose(bool disposing)
\r
1168 base.Dispose(disposing);
\r
1171 private void CleanUp()
\r
1173 if (_currentReader != null) {
\r
1174 // we must preserve statement object until we have an associated reader object that might access it.
\r
1177 if (Connection != null) {
\r
1178 ((AbstractDBConnection)Connection).RemoveReference(this);
\r
1180 if (_statement != null) {
\r
1181 _statement.close();
\r
1182 _statement = null;
\r
1184 IsCommandPrepared = false;
\r
1185 _internalParameters = null;
\r
1186 _currentResultSet = null;
\r
1189 internal void OnSchemaChanging()
\r
1193 #endregion // Methods
\r