Inserting a duplicate record using Npgsql - c#

I'm trying to insert data into the following table using the Npgsql ADO.NET provider:
CREATE SCHEMA core;
CREATE TABLE core.config(
name TEXT NOT NULL,
value TEXT NOT NULL,
CONSTRAINT pk_config PRIMARY KEY (name)
);
The first insert works fine but if I try to insert an item with the same name again, Npgsql just hangs. I would expect an exception to be thrown stating that the primary key constraint has been violated, but Npgsql just freezes instead. Sure enough, if I check the server logs I see this:
duplicate key value violates unique constraint "pk_config"
but this message doesn't seem to be getting back to Npgsql. Am I doing something wrong? My code is here:
using (NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;Database=temp;User Id=postgres;Password=test1"))
{
conn.Open();
string cmdText = "INSERT INTO core.config(name, value) values ('item', 'value')";
using (NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn))
{
cmd.ExecuteNonQuery(); // This works fine
cmd.ExecuteNonQuery(); // Insert again: this hangs, but would expect an exception
}
}
This is a simplified version of a larger application (hence the use of a schema). I then tried creating the table in public (no schema specified). Interestingly this DOES generate an exception on the second call to ExecuteNonQuery():
Backend sent unrecognized response type
I'm a little lost as to whether this is a bug in Npgsql or whether it is something to do with Schema privileges (even though for this test I'm using the "postgres" superuser).
I'm using Npgsql 2.0.12.0

We have the same problem. According to this thread:
http://pgfoundry.org/forum/forum.php?set=custom&forum_id=518&style=nested&max_rows=50&submit=Change+View
We just noticed that Npgsql has a problem when handling error messages from postgresql 9.3.x.
Glen Parker already fixed in our latest code: https://github.com/npgsql/Npgsql/pull/99. We are working to create a new 2.0.12 stable release with this fix.
If you want to give it a try you can get the latest git code and check if it works ok for you. It is in beta state though.
Please, let me know if you have any problems with it.
So if you are using PostgreSQL 9.3, this may be your issue. You'll have to download the fix and experiment to see if this is actually your issue in this case.

Related

Minimum access levels to run MySql stored procedure

I am trying to setup my .NET 4.7.1 program that is connecting to a MySQL database 8.0 to use the minimum privileges to run.
The .NET program is using MySql.Data to make connection. The minimum right for a user to execute a stored procedure is typically only EXECUTE privilege. This works fine from MySQL workbench or command line.
Upon running the .NET program this does return the following exception:
System.Data.SqlTypes.SqlNullValueException: 'Data is Null. This method or property cannot be called on Null values.'
To make it easy, I have create a very small demo program to demonstrate the issue.
Setup of the database:
CREATE DATABASE Spike;
CREATE PROCEDURE TestAccess()
BEGIN
END;
CREATE USER Spike#localhost IDENTIFIED WITH mysql_native_password BY 'sample';
GRANT EXECUTE ON PROCEDURE `TestAccess` TO Spike#localhost;
Setup program code:
static void Main(string[] args)
{
using (MySqlConnection conn = new MySqlConnection("Server=localhost;Database=Spike;uid=Spike;pwd=sample"))
{
conn.Open();
Console.WriteLine("Connection open");
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "TestAccess";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
Console.WriteLine("Query executed");
}
Console.ReadKey();
}
The crash happens at the line cmd.ExecuteNonQuery();
The stack from the crash is interesting, since it seems to indicate that the information_schema is queried. When logging all statements I can see that the last statement before the exception is:
SELECT * FROM information_schema.routines WHERE 1=1 AND routine_schema LIKE 'Spike' AND routine_name LIKE 'TestAccess'
I cannot grant different rights on information_schema, but I could give more rights on the stored procedure to make more information visible in the routines table, this feels wrong however. Simple tests with granting CREATE and ALTER access also did not work.
Is there something else I can do, without granting too much privileges?
This appears to be a bug in Connector/NET, similar to bug 75301 but a little different. When it's trying to determine parameter metadata for the procedure, it first creates a MySqlSchemaCollection named Procedures with all metadata about the procedure. (This is the SELECT * FROM information_schema.routines WHERE 1=1 AND routine_schema LIKE 'Spike' AND routine_name LIKE 'TestAccess' query you see in your log.)
The Spike user account doesn't have permission to read the ROUTINE_DEFINITION column, so it is NULL. Connector/NET expects this field to be non-NULL and throws a SqlNullValueException exception trying to read it.
There are two workarounds:
1) The first, which you've discovered, is to set CheckParameters=False in your connection string. This will disable retrieval of stored procedure metadata (avoiding the crash), but may lead to harder-to-debug problems calling other stored procedures if you don't get the order and type of parameters exactly right. (Connector/NET can no longer map them for you using the metadata.)
2) Switch to a different ADO.NET MySQL library that doesn't have this bug: MySqlConnector on NuGet. It's highly compatible with Connector/NET, performs faster, and fixes a lot of known issues.
I found an answer with which I am quite pleased. It is changing the connection string by adding CheckParameters=false:
using (MySqlConnection conn = new MySqlConnection("Server=localhost;Database=Spike;uid=Spike;pwd=sample;CheckParameters=false"))
This disables parameter checking, and thereby information_schema queries.

C#/SQL Error "Object not set to an instance of an object"

This may not actually be a code issue, but here goes...
My C# program reads a CSV file and builds an SQL Insert statement to insert all rows of the file into a table, so the Insert statement becomes:
Insert GHP_For_CMS (GroupId, EmployeeID, etc...) values (rec1_field1, rec1_field2, etc...), (rec2_field1, rec2_field2, etc...)
SQL runs flawlessly against a Sandbox/Dev db from a Dev Server. SQL crashes with a NullReference exception ("Object not set to an instance of an object") against the same Sandbox/Dev db from a Production server. The Dev server has Framework version 4.0.30319.18052, and the Production has Framework version 4.0.30319.18063.
I know I haven't given a lot of information here, but anyone have any ideas? Or at least ideas of something to check? I've been banging my head against this one for 3-4 days and nothing is rearing it's ugly head.
TIA!
The error happens on the 'ExecuteNonQuery' below...
string dbConnString = ConfigurationManager.ConnectionStrings[targetDatabase].ToString();
if (null != dbConnString)
{
using (SqlConnection dbConn = new SqlConnection(dbConnString))
{
if (dbConn != null)
{
SqlCommand cmd = new SqlCommand(m_sqlInserts.ToString(), dbConn);
dbConn.Open();
int rowsAdded = cmd.ExecuteNonQuery();
...
So the answer to the question (because I couldn't post more details and I really wanted to know of things to try): is that it's a simple Permissions issue with the account that is running the process not being able to insert records to the table. The comment that helped me get to that conclusion came from #cdonner (Thank you, again!) in wanting to see a stack trace. I realized that I could return a StackTrace in the console log instead of just the InnerException message and it (the SQL Error) was finally revealed.

DROP command denied to user X for table 'Y'

We are getting ready for a big SQL migration.
Currently, I have the code written, and I am testing it out with data on my local machine.
Step 1 is to throw out the existing data in the table before I import the new stuff:
using (var txn = m_mySqlConnection.BeginTransaction()) {
using (var cmd = new MySqlCommand("TRUNCATE TABLE `blah_blah`;", m_mySqlConnection, txn)) {
cmd.ExecuteNonQuery();
}
// other code
}
But, the TRUNCATE command is throwing an exception whenever I try to execute it with the MySQL user account I am running the code with:
I tried going into MySQL Workbench to give this userid DROP permission, but all I could find was a way to add DROP under the View section.
I tried that, but it did not work.
How do I go about giving this user the ability to remove the data in these tables so that I can test my populate script?
TRUNCATE deletes the table. Try using DELETE FROM Table.

Paramertized SQL, Oracle 11g, and C# Failing to create index

I am trying to use a parametrized query to create an index on our Oracle server. I can create the index fine if I use string concatenation, so I believe its not an account or permissions issue. I get the error:
ORA-01036: illegal variable name/number
I can not find any error in the code, but I am sure I am missing something. I am using Oracle.DataAccess.dll version 4.112.3.0. The version the server reports is "Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production"
I have tried removing all the params, except one and get the same error. Any combination of 1 or more params causes the error. The error happens every time without fail. I have used watches to verify that the params are correctly getting set, and I have tried clearing the params before setting. No matter what I do so far, I still get the error. Here is the code:
using (OracleCommand mycom = new OracleCommand())
{
using (OracleConnection Connection = new OracleConnection(connectionString))
{
mycom.BindByName = true;
mycom.CommandText = "CREATE INDEX :indexname on :tablename (:colname)";
mycom.Parameters.Add("indexname", indexName);
mycom.Parameters.Add("tablename", tableName);
mycom.Parameters.Add("colname", colName);
mycom.Connection = Connection;
mycom.CommandType = CommandType.Text;
mycom.Connection.Open();
mycom.ExecuteNonQuery();
}
}
You cannot use bind variables in a DDL statement (like CREATE INDEX). Bind variables are only used in DML statements. You would need to build up the DDL statement in your C# code.
Is this a part of the installation process for your application? Otherwise, it would be exceedingly unusual to want to create an index from an application. Your application shouldn't be creating any database objects outside of the initial installation process.

SQL CLR: Streaming table valued function results

My issue is very similar to this issue.
However, I'm using SQL Server 2005 Service Pack 2 (SP2) (v9.0.3042) and the solution posted there does not work for me. I tried using both connection strings. One is commented out in my code.
I realize I can store all the results in a List or ArrayList in memory and return that. I've done that successfully, but that is not the goal here. The goal is to be able to stream the results as they are available.
Is this possible using my version of SQL Server?
Here's my code :
(Note that the parameters aren't actually being used currently. I did this for debugging)
public static class StoredProcs
{
[SqlFunction(
DataAccess = DataAccessKind.Read,
SystemDataAccess=SystemDataAccessKind.Read,
FillRowMethodName="FillBaseline",
TableDefinition = "[baseline_id] [int], [baseline_name] [nvarchar](256), [description] [nvarchar](max), [locked] [bit]"
)]
public static IEnumerable fnGetBaselineByID(SqlString projectName, SqlInt32 baselineID)
{
string connStr = "context connection=true";
//string connStr = "data source=.;initial catalog=DBName;integrated security=SSPI;enlist=false";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(String.Format(#"
SELECT *
FROM [DBName].[dbo].[Baseline] WITH (NOLOCK)
"), conn))
{
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return new Baseline(reader);
}
}
}
};
}
public static void FillBaseline(Object obj, out SqlInt32 id, out SqlString name, out SqlString description, out bool locked)
{
Baseline baseline = (Baseline)obj;
id = baseline.mID;
name = baseline.nName;
description = baseline.mDescription;
locked = baseline.mLocked;
}
}
Here's part of my SQL deploy script:
CREATE ASSEMBLY [MyService_Stored_Procs]
FROM 'C:\temp\assemblyName.dll'
WITH PERMISSION_SET = SAFE
When I use the connection string "context connection=true" I get this error:
An error occurred while getting new row from user defined Table
Valued Function :
System.InvalidOperationException: Data access is not allowed in
this context. Either the context is a function or method not marked
with DataAccessKind.Read or SystemDataAccessKind.Read, is a callback
to obtain data from FillRow method of a Table Valued Function, or is a
UDT validation method.
When I use the other connection string I get this error:
An error occurred while getting new row from user defined Table
Valued Function :
System.Security.SecurityException: Request for the permission of
type 'System.Data.SqlClient.SqlClientPermission, System.Data,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
failed.
Upon further research and trial and error I found my solution. The article that I mentioned here says
your assembly must be created with permission_set=external_access
This is much easier said than done, but was a good starting point. Simply using that line in place of permission_set=safe gives the error:
CREATE ASSEMBLY for assembly 'assemblyName' failed because assembly
'assemblyName' is not authorized for PERMISSION_SET = EXTERNAL_ACCESS.
The assembly is authorized when either of the following is true: the
database owner (DBO) has EXTERNAL ACCESS ASSEMBLY permission and the
database has the TRUSTWORTHY database property on; or the assembly is
signed with a certificate or an asymmetric key that has a
corresponding login with EXTERNAL ACCESS ASSEMBLY permission.
So the first thing I had to do was sign my dll file. To do that in Visual Studio 2010, you go to the project properties, Signing tab, and check "Sign the assembly" and give it a name. For this example, the name is MyDllKey. I chose not to protect it with a password. Then, of course, I copied the dll file to the sql server: C:\Temp
Using this page as a reference, I created a SQL login based on the above key using these 3 commands:
CREATE ASYMMETRIC KEY MyDllKey FROM EXECUTABLE FILE = 'C:\Temp\MyDll.dll'
CREATE LOGIN MyDllLogin FROM ASYMMETRIC KEY MyDllKey
GRANT EXTERNAL ACCESS ASSEMBLY TO MyDllLogin
Once the login is created as above, I can now create the assembly using this:
CREATE ASSEMBLY [MyDll]
FROM 'C:\Temp\MyDll.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS
Now the only thing left to do is use the proper connection string. Apparently using enlist=false in combination with connection=true is not possible. Here is an example of the connection string I used.
string connStr = #"data source=serverName\instanceName;initial catalog=DBName;integrated security=SSPI;enlist=false";
And it works!
The original problem is due to use of the yield keyword within your function, as explained in this question: SqlFunction fails to open context connection despite DataAccessKind.Read present.
If you avoid using yield (store results in an intermediate array, return the whole lot at the end) the problem goes away.
Alternatively you can do as you describe and avoid using the context connection, but if you do that you have to mark your assembly for external access as you describe. I think that's best described as a workaround, rather than a solution, given you lose some of the benefits available from a context connection and because of all the extra hoops you have to jump through.
In many cases the benefit of being able to use streaming behaviour (yield) does outweigh this pain, but it's still worth considering both options.
Here's the bug on Connect: http://connect.microsoft.com/SQLServer/feedback/details/442200/sql-server-2008-clr-tvf-data-access-limitations-break-existing-code
Googling for this:
Data access is not allowed in this context. Either the context is a
function or method not marked with DataAccessKind.Read or
SystemDataAccessKind.Read, is a callback to obtain data from FillRow
method of a Table Valued Function, or is a UDT validation method.
Led me to this page, but without the answer I needed.
I eventually figured out what it was.
In my CLR Function, I was calling another Method and passing in the values the Function had recieved.
Sounds innocuous, but what I had done was used the same datatypes (SqlChars, SqlBoolean, SqlInt32) for the input-parameters of the Method I added.
private static ArrayList FlatFile(SqlChars Delimeter, SqlChars TextQualifier)
Apparently using these datatypes for anything other than a CLR SqlFunction or SqlProcedure can sometimes give you this type of cryptic error.
Once I removed those datatypes on my new Method and used the C# ones (string, bool, int), the error finally went away.
private static ArrayList FlatFile(string Delimeter, string TextQualifier)
NOTE: This only errored out when I was using Impersonation to grab a file from another Domain.
When I streamed the file over the local Domain, I didn't receive this error, which is what threw me off.
I hope this helps you in your time of need. I blew way too many hours troubleshooting this.

Categories