Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancisco Figueiredo Jr. <fxjr@mono-cvs.ximian.com>2006-04-27 18:27:17 +0400
committerFrancisco Figueiredo Jr. <fxjr@mono-cvs.ximian.com>2006-04-27 18:27:17 +0400
commit115618cbd47ca87d2aa37614ce73a6b502fc2266 (patch)
treecb2940cee1b6bd00e2579018b626f33a40f23bd4 /mcs/class/Npgsql
parent8e24a975c8cd4621d169e3b6597169bb5158ea8f (diff)
2006-04-27 Francisco Figueiredo Jr. <fxjrlist@yahoo.com.br>
* NpgsqlCommand.cs: Commited wrong file previously. This is the correct one. svn path=/trunk/mcs/; revision=59982
Diffstat (limited to 'mcs/class/Npgsql')
-rw-r--r--mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs289
1 files changed, 209 insertions, 80 deletions
diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs b/mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs
index 3859931f5c7..e64bdc2a951 100644
--- a/mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs
+++ b/mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs
@@ -52,6 +52,7 @@ namespace Npgsql
// Logging related values
private static readonly String CLASSNAME = "NpgsqlCommand";
private static ResourceManager resman = new ResourceManager(typeof(NpgsqlCommand));
+ private static readonly Regex parameterReplace = new Regex(@"([:@][\w\.]*)", RegexOptions.Singleline);
private NpgsqlConnection connection;
private NpgsqlConnector connector;
@@ -69,6 +70,8 @@ namespace Npgsql
private CommandBehavior commandBehavior;
+ private Int64 lastInsertedOID = 0;
+
// Constructors
/// <summary>
@@ -347,11 +350,23 @@ namespace Npgsql
set
{
- throw new NotImplementedException();
}
}
/// <summary>
+ /// Returns oid of inserted row. This is only updated when using executenonQuery and when command inserts just a single row. If table is created without oids, this will always be 0.
+ /// </summary>
+
+ public Int64 LastInsertedOID
+ {
+ get
+ {
+ return lastInsertedOID;
+ }
+ }
+
+
+ /// <summary>
/// Attempts to cancel the execution of a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
/// </summary>
/// <remarks>This Method isn't implemented yet.</remarks>
@@ -413,6 +428,9 @@ namespace Npgsql
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteNonQuery");
+ // Initialize lastInsertOID
+ lastInsertedOID = 0;
+
ExecuteCommand();
UpdateOutputParameters();
@@ -433,7 +451,7 @@ namespace Npgsql
String[] ret_string_tokens = firstCompletedResponse.Split(null); // whitespace separator.
- // Check if the command was insert, delete or update.
+ // Check if the command was insert, delete, update, fetch or move.
// Only theses commands return rows affected.
// [FIXME] Is there a better way to check this??
if ((String.Compare(ret_string_tokens[0], "INSERT", true) == 0) ||
@@ -441,12 +459,19 @@ namespace Npgsql
(String.Compare(ret_string_tokens[0], "DELETE", true) == 0) ||
(String.Compare(ret_string_tokens[0], "FETCH", true) == 0) ||
(String.Compare(ret_string_tokens[0], "MOVE", true) == 0))
-
+
+
+ {
+ if (String.Compare(ret_string_tokens[0], "INSERT", true) == 0)
+ // Get oid of inserted row.
+ lastInsertedOID = Int32.Parse(ret_string_tokens[1]);
+
// The number of rows affected is in the third token for insert queries
// and in the second token for update and delete queries.
// In other words, it is the last token in the 0-based array.
return Int32.Parse(ret_string_tokens[ret_string_tokens.Length - 1]);
+ }
else
return -1;
}
@@ -600,19 +625,37 @@ namespace Npgsql
if (parameters.Count != 0)
{
Object[] parameterValues = new Object[parameters.Count];
+ Int16[] parameterFormatCodes = bind.ParameterFormatCodes;
+
for (Int32 i = 0; i < parameters.Count; i++)
{
// Do not quote strings, or escape existing quotes - this will be handled by the backend.
// DBNull or null values are returned as null.
// TODO: Would it be better to remove this null special handling out of ConvertToBackend??
- parameterValues[i] = parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, true);
+
+ // Do special handling of bytea values. They will be send in binary form.
+ // TODO: Add binary format support for all supported types. Not only bytea.
+ if (parameters[i].TypeInfo.NpgsqlDbType != NpgsqlDbType.Bytea)
+ {
+
+ parameterValues[i] = parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, true);
+ }
+ else
+ {
+ parameterFormatCodes[i] = (Int16) FormatCode.Binary;
+ parameterValues[i]=(byte[])parameters[i].Value;
+ }
}
bind.ParameterValues = parameterValues;
+ bind.ParameterFormatCodes = parameterFormatCodes;
}
Connector.Bind(bind);
+
+ // See Prepare() method for a discussion of this.
Connector.Mediator.RequireReadyForQuery = false;
Connector.Flush();
+
connector.CheckErrorsAndNotifications();
}
@@ -627,18 +670,6 @@ namespace Npgsql
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteScalar");
- /*if ((type == CommandType.Text) || (type == CommandType.StoredProcedure))
- if (parse == null)
- connection.Query(this);
- else
- {
- BindParameters();
- connection.Execute(new NpgsqlExecute(bind.PortalName, 0));
- }
- else
- throw new NotImplementedException(resman.GetString("Exception_CommandTypeTableDirect"));
- */
-
ExecuteCommand();
// Now get the results.
@@ -675,8 +706,14 @@ namespace Npgsql
// Check the connection state.
CheckConnectionState();
+
+ // reset any responses just before getting new ones
+ Connector.Mediator.ResetResponses();
+
+ // Set command timeout.
+ connector.Mediator.CommandTimeout = CommandTimeout;
- if (! Connector.SupportsPrepare)
+ if (! connector.SupportsPrepare)
{
return; // Do nothing.
}
@@ -688,21 +725,86 @@ namespace Npgsql
}
else
{
- // Use the extended query parsing...
- //planName = "NpgsqlPlan" + Connector.NextPlanIndex();
- planName = Connector.NextPlanName();
- String portalName = Connector.NextPortalName();
-
- parse = new NpgsqlParse(planName, GetParseCommandText(), new Int32[] {});
-
- Connector.Parse(parse);
- Connector.Mediator.RequireReadyForQuery = false;
- Connector.Flush();
-
- // Check for errors and/or notifications and do the Right Thing.
- connector.CheckErrorsAndNotifications();
-
- bind = new NpgsqlBind("", planName, new Int16[] {0}, null, new Int16[] {0});
+ try
+ {
+
+ connector.StopNotificationThread();
+
+ // Use the extended query parsing...
+ planName = connector.NextPlanName();
+ String portalName = connector.NextPortalName();
+
+ parse = new NpgsqlParse(planName, GetParseCommandText(), new Int32[] {});
+
+ connector.Parse(parse);
+
+ // We need that because Flush() doesn't cause backend to send
+ // ReadyForQuery on error. Without ReadyForQuery, we don't return
+ // from query extended processing.
+
+ // We could have used Connector.Flush() which sends us back a
+ // ReadyForQuery, but on postgresql server below 8.1 there is an error
+ // with extended query processing which hinders us from using it.
+ connector.Mediator.RequireReadyForQuery = false;
+ connector.Flush();
+
+ // Check for errors and/or notifications and do the Right Thing.
+ connector.CheckErrorsAndNotifications();
+
+
+ // Description...
+ NpgsqlDescribe describe = new NpgsqlDescribe('S', planName);
+
+
+ connector.Describe(describe);
+
+ connector.Sync();
+
+ Npgsql.NpgsqlRowDescription returnRowDesc = connector.Mediator.LastRowDescription;
+
+ Int16[] resultFormatCodes;
+
+
+ if (returnRowDesc != null)
+ {
+ resultFormatCodes = new Int16[returnRowDesc.NumFields];
+
+ for (int i=0; i < returnRowDesc.NumFields; i++)
+ {
+ Npgsql.NpgsqlRowDescriptionFieldData returnRowDescData = returnRowDesc[i];
+
+
+ if (returnRowDescData.type_info != null && returnRowDescData.type_info.NpgsqlDbType == NpgsqlTypes.NpgsqlDbType.Bytea)
+ {
+ // Binary format
+ resultFormatCodes[i] = (Int16)FormatCode.Binary;
+ }
+ else
+ // Text Format
+ resultFormatCodes[i] = (Int16)FormatCode.Text;
+ }
+
+
+ }
+ else
+ resultFormatCodes = new Int16[]{0};
+
+ bind = new NpgsqlBind("", planName, new Int16[Parameters.Count], null, resultFormatCodes);
+ }
+ catch
+ {
+ // See ExecuteCommand method for a discussion of this.
+ connector.Sync();
+
+ throw;
+ }
+ finally
+ {
+ connector.ResumeNotificationThread();
+ }
+
+
+
}
}
@@ -833,28 +935,22 @@ namespace Npgsql
// If parenthesis don't need to be added, they were added by user with parameter names. Replace them.
if (!addProcedureParenthesis)
{
-
- Regex a = new Regex(@"(:[\w]*)|(@[\w]*)|(.)", RegexOptions.Singleline);
-
- //CheckParameters();
-
StringBuilder sb = new StringBuilder();
+ NpgsqlParameter p;
+ string[] queryparts = parameterReplace.Split(result);
- for ( Match m = a.Match(result); m.Success; m = m.NextMatch() )
+ foreach (String s in queryparts)
{
- String s = m.Groups[0].ToString();
+ if (s == string.Empty)
+ continue;
- if ((s.StartsWith(":") ||
- s.StartsWith("@")) &&
- Parameters.Contains(s))
+ if ((s[0] == ':' || s[0] == '@') &&
+ Parameters.TryGetValue(s, out p))
{
// It's a parameter. Lets handle it.
-
- NpgsqlParameter p = Parameters[s];
if ((p.Direction == ParameterDirection.Input) ||
(p.Direction == ParameterDirection.InputOutput))
{
-
// FIXME DEBUG ONLY
// adding the '::<datatype>' on the end of a parameter is a highly
// questionable practice, but it is great for debugging!
@@ -875,13 +971,11 @@ namespace Npgsql
}
else
sb.Append(s);
-
}
result = sb.ToString();
}
-
else
{
@@ -957,6 +1051,10 @@ namespace Npgsql
// reset any responses just before getting new ones
connector.Mediator.ResetResponses();
+
+ // Set command timeout.
+ connector.Mediator.CommandTimeout = CommandTimeout;
+
return ret;
@@ -1019,6 +1117,9 @@ namespace Npgsql
// reset any responses just before getting new ones
connector.Mediator.ResetResponses();
+ // Set command timeout.
+ connector.Mediator.CommandTimeout = CommandTimeout;
+
return sb.ToString();
@@ -1163,7 +1264,7 @@ namespace Npgsql
}
- //[TODO] Check if there is any missing parameters in the query.
+ //[TODO] Check if there are any missing parameters in the query.
// For while, an error is thrown saying about the ':' char.
command.Append('(');
@@ -1193,41 +1294,51 @@ namespace Npgsql
}
- private String ReplaceParameterValue(String result, String parameterName, String paramVal)
+ private static String ReplaceParameterValue(String result, String parameterName, String paramVal)
{
- Int32 resLen = result.Length;
- Int32 paramStart = result.IndexOf(parameterName);
- Int32 paramLen = parameterName.Length;
- Int32 paramEnd = paramStart + paramLen;
+
+ String quote_pattern = @"['][^']*[']";
+ String pattern = "[- |\n\r\t,)(;=+/]" + parameterName + "([- |\n\r\t,)(;=+/]|$)";
+ Int32 start, end;
+ String withoutquote = result;
Boolean found = false;
-
-
- while(paramStart > -1)
+ // First of all
+ // Suppress quoted string from query (because we ave to ignore them)
+ MatchCollection results = Regex.Matches(result,quote_pattern);
+ foreach (Match match in results)
{
- if((resLen > paramEnd) && !Char.IsLetterOrDigit(result, paramEnd))
- {
- result = result.Substring(0, paramStart) + paramVal + result.Substring(paramEnd);
- found = true;
- }
- else if(resLen == paramEnd)
- {
- result = result.Substring(0, paramStart)+ paramVal;
- found = true;
- }
- else
+ start = match.Index;
+ end = match.Index + match.Length;
+ String spaces = new String(' ', match.Length-2);
+ withoutquote = withoutquote.Substring(0,start + 1) + spaces + withoutquote.Substring(end - 1);
+ }
+ do
+ {
+ // Now we look for the searched parameters on the "withoutquote" string
+ results = Regex.Matches(withoutquote,pattern);
+ if (results.Count == 0)
+ // If no parameter is found, go out!
break;
- resLen = result.Length;
- paramStart = result.IndexOf(parameterName, paramStart);
- paramEnd = paramStart + paramLen;
-
- }//while
- if(!found)
- throw new IndexOutOfRangeException (String.Format(resman.GetString("Exception_ParamNotInQuery"), parameterName));
-
-
+ // We take the first parameter found
+ found = true;
+ Match match = results[0];
+ start = match.Index;
+ if ((match.Length - parameterName.Length) == 2)
+ // If the found string is not the end of the string
+ end = match.Index + match.Length - 1;
+ else
+ // If the found string is the end of the string
+ end = match.Index + match.Length;
+ result = result.Substring(0, start + 1) + paramVal + result.Substring(end);
+ withoutquote = withoutquote.Substring(0,start + 1) + paramVal + withoutquote.Substring(end);
+ }
+ while (true);
+ if (!found)
+ throw new IndexOutOfRangeException (String.Format(resman.GetString("Exception_ParamNotInQuery"),
+ parameterName));
return result;
- }//ReplaceParameterValue
-
+ }
+
private String AddSingleRowBehaviorSupport(String ResultCommandText)
{
@@ -1279,14 +1390,26 @@ namespace Npgsql
// reset any responses just before getting new ones
Connector.Mediator.ResetResponses();
+
+ // Set command timeout.
+ connector.Mediator.CommandTimeout = CommandTimeout;
+
+
+ connector.StopNotificationThread();
if (parse == null)
{
- Connector.Query(this);
+ connector.Query(this);
+
+ connector.ResumeNotificationThread();
+
// Check for errors and/or notifications and do the Right Thing.
connector.CheckErrorsAndNotifications();
+
+
+
}
else
{
@@ -1300,7 +1423,7 @@ namespace Npgsql
// Check for errors and/or notifications and do the Right Thing.
connector.CheckErrorsAndNotifications();
}
- finally
+ catch
{
// As per documentation:
// "[...] When an error is detected while processing any extended-query message,
@@ -1309,6 +1432,12 @@ namespace Npgsql
// So, send a sync command if we get any problems.
connector.Sync();
+
+ throw;
+ }
+ finally
+ {
+ connector.ResumeNotificationThread();
}
}
}