diff --git a/Npgsql.EntityFramework/Npgsql.EntityFramework.csproj b/Npgsql.EntityFramework/Npgsql.EntityFramework.csproj
index 8c6e5dd0fa..b0599a4c54 100644
--- a/Npgsql.EntityFramework/Npgsql.EntityFramework.csproj
+++ b/Npgsql.EntityFramework/Npgsql.EntityFramework.csproj
@@ -9,6 +9,7 @@
Library
Properties
Npgsql.EntityFramework
+ Npgsql
512
true
..\Npgsql.snk
@@ -97,6 +98,7 @@
+
diff --git a/Npgsql.EntityFramework/Npgsql.EntityFrameworkLegacy.csproj b/Npgsql.EntityFramework/Npgsql.EntityFrameworkLegacy.csproj
index 5ec3323653..965be3e111 100644
--- a/Npgsql.EntityFramework/Npgsql.EntityFrameworkLegacy.csproj
+++ b/Npgsql.EntityFramework/Npgsql.EntityFrameworkLegacy.csproj
@@ -9,6 +9,7 @@
Library
Properties
Npgsql.EntityFrameworkLegacy
+ Npgsql
512
true
..\Npgsql.snk
diff --git a/Npgsql.EntityFramework/NpgsqlConnectionFactory.cs b/Npgsql.EntityFramework/NpgsqlConnectionFactory.cs
new file mode 100755
index 0000000000..1d5e4994be
--- /dev/null
+++ b/Npgsql.EntityFramework/NpgsqlConnectionFactory.cs
@@ -0,0 +1,21 @@
+using System.Data.Common;
+using System.Data.Entity.Infrastructure;
+
+namespace Npgsql
+{
+ ///
+ /// Instances of this class are used to create DbConnection objects for Postgresql
+ ///
+ public class NpgsqlConnectionFactory : IDbConnectionFactory
+ {
+ ///
+ /// Creates a connection for Postgresql for the given connection string.
+ ///
+ /// The connection string.
+ /// An initialized DbConnection.
+ public DbConnection CreateConnection(string nameOrConnectionString)
+ {
+ return new NpgsqlConnection(nameOrConnectionString);
+ }
+ }
+}
diff --git a/Npgsql.EntityFramework/NpgsqlProviderManifest.cs b/Npgsql.EntityFramework/NpgsqlProviderManifest.cs
index 0d4351522e..6ff4223075 100644
--- a/Npgsql.EntityFramework/NpgsqlProviderManifest.cs
+++ b/Npgsql.EntityFramework/NpgsqlProviderManifest.cs
@@ -16,7 +16,7 @@ namespace Npgsql
internal class NpgsqlProviderManifest : DbXmlEnabledProviderManifest
{
public NpgsqlProviderManifest(string serverVersion)
- : base(CreateXmlReaderForResource("NpgsqlProviderManifest.Manifest.xml"))
+ : base(CreateXmlReaderForResource("Npgsql.NpgsqlProviderManifest.Manifest.xml"))
{
}
@@ -26,11 +26,11 @@ protected override XmlReader GetDbInformation(string informationType)
if (informationType == StoreSchemaDefinition)
{
- xmlReader = CreateXmlReaderForResource("NpgsqlSchema.ssdl");
+ xmlReader = CreateXmlReaderForResource("Npgsql.NpgsqlSchema.ssdl");
}
else if (informationType == StoreSchemaMapping)
{
- xmlReader = CreateXmlReaderForResource("NpgsqlSchema.msl");
+ xmlReader = CreateXmlReaderForResource("Npgsql.NpgsqlSchema.msl");
}
if (xmlReader == null)
diff --git a/Npgsql/Npgsql.csproj b/Npgsql/Npgsql.csproj
index 999fbd150b..cfce0670cf 100644
--- a/Npgsql/Npgsql.csproj
+++ b/Npgsql/Npgsql.csproj
@@ -9,6 +9,7 @@
Library
Properties
Npgsql
+ Npgsql
512
true
..\Npgsql.snk
diff --git a/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs b/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs
index 0524aeebde..647e8bc363 100644
--- a/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs
+++ b/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs
@@ -558,7 +558,8 @@ private enum TokenType
None,
Quoted,
Param,
- Colon
+ Colon,
+ FullTextMatchOp
}
///
@@ -579,6 +580,7 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int
int currTokenBeg = begin;
int currTokenLen = 0;
Dictionary paramOrdinalMap = null;
+ int end = begin + length;
if (prepare)
{
@@ -590,7 +592,7 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int
}
}
- for (int currCharOfs = begin ; currCharOfs < begin + length ; currCharOfs++)
+ for (int currCharOfs = begin ; currCharOfs < end ; currCharOfs++)
{
char ch = src[currCharOfs];
@@ -602,7 +604,7 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int
case TokenType.None :
switch (ch)
{
- case '\'':
+ case '\'' :
if (currTokenLen > 0)
{
dest.WriteString(src.Substring(currTokenBeg, currTokenLen));
@@ -615,7 +617,8 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int
break;
- case ':':
+ case ':' :
+ if (currTokenLen > 0)
{
dest.WriteString(src.Substring(currTokenBeg, currTokenLen));
}
@@ -627,20 +630,21 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int
break;
- case '@':
+ case '<' :
+ case '@' :
+ if (currTokenLen > 0)
{
dest.WriteString(src.Substring(currTokenBeg, currTokenLen));
}
- currTokenType = TokenType.Param;
+ currTokenType = TokenType.FullTextMatchOp;
- currTokenBeg = currCharOfs + 1;
- currTokenLen = 0;
- paramMarker = '@';
+ currTokenBeg = currCharOfs;
+ currTokenLen = 1;
break;
- default:
+ default :
currTokenLen++;
break;
@@ -705,12 +709,12 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int
case TokenType.Quoted :
switch (ch)
{
- case '\'':
+ case '\'' :
currTokenLen++;
break;
- default:
+ default :
if (currTokenLen > 1 && lastChar == '\'')
{
dest.WriteString(src.Substring(currTokenBeg, currTokenLen));
@@ -734,40 +738,47 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int
break;
case TokenType.Colon :
- switch (ch)
+ if (IsParamNameChar(ch))
{
- case ':':
- currTokenLen++;
-
- break;
+ currTokenType = TokenType.Param;
- default:
- if (currTokenLen == 1)
- {
- currTokenType = TokenType.Param;
+ currTokenBeg = currCharOfs;
+ currTokenLen = 0;
+ paramMarker = ':';
- currTokenBeg = currCharOfs;
- currTokenLen = 0;
- paramMarker = ':';
- }
- else
- {
- dest.WriteString(src.Substring(currTokenBeg, currTokenLen));
+ // Re-evaluate this character
+ goto ProcessCharacter;
+ }
+ else
+ {
+ // Demote to the unknown token type and continue.
+ currTokenType = TokenType.None;
+ currTokenLen++;
+ }
- currTokenType = TokenType.None;
+ break;
- currTokenBeg = currCharOfs;
- currTokenLen = 0;
- }
+ case TokenType.FullTextMatchOp :
+ if (lastChar == '@' && IsParamNameChar(ch))
+ {
+ currTokenType = TokenType.Param;
- // Re-evaluate this character
- goto ProcessCharacter;
+ currTokenBeg = currCharOfs;
+ currTokenLen = 0;
+ paramMarker = '@';
+ // Re-evaluate this character
+ goto ProcessCharacter;
+ }
+ else
+ {
+ // Demote to the unknown token type and continue.
+ currTokenType = TokenType.None;
+ currTokenLen++;
}
break;
-
}
lastChar = ch;
diff --git a/Npgsql/Npgsql/NpgsqlRowDescription.cs b/Npgsql/Npgsql/NpgsqlRowDescription.cs
index 533a32cb25..e213e2de8c 100644
--- a/Npgsql/Npgsql/NpgsqlRowDescription.cs
+++ b/Npgsql/Npgsql/NpgsqlRowDescription.cs
@@ -32,6 +32,7 @@
using System.IO;
using System.Text;
using NpgsqlTypes;
+using System.Text.RegularExpressions;
namespace Npgsql
{
@@ -196,12 +197,26 @@ public int TryFieldIndex(string fieldName)
public int FieldIndex(String fieldName)
{
int ret = -1;
- if(field_name_index_table.TryGetValue(fieldName, out ret) || caseInsensitiveNameIndexTable.TryGetValue(fieldName, out ret))
+
+ if (field_name_index_table.TryGetValue(fieldName, out ret)
+ || caseInsensitiveNameIndexTable.TryGetValue(fieldName, out ret))
+ return ret;
+
+ string fieldNameUnderScore = ConvertToUnderscore(fieldName);
+ if(field_name_index_table.TryGetValue(fieldNameUnderScore, out ret)
+ || caseInsensitiveNameIndexTable.TryGetValue(fieldNameUnderScore, out ret))
return ret;
- else if(_compatVersion < GET_ORDINAL_THROW_EXCEPTION)
+
+ else if (_compatVersion < GET_ORDINAL_THROW_EXCEPTION)
return -1;
else
throw new IndexOutOfRangeException("Field not found");
+
+ }
+
+ private string ConvertToUnderscore(string val)
+ {
+ return Regex.Replace(val, @"(\p{Ll})(\p{Lu})", "$1_$2");
}
}
diff --git a/Npgsql/Npgsql/NpgsqlSchema.cs b/Npgsql/Npgsql/NpgsqlSchema.cs
index a7d8ce292c..67422f6e59 100644
--- a/Npgsql/Npgsql/NpgsqlSchema.cs
+++ b/Npgsql/Npgsql/NpgsqlSchema.cs
@@ -411,7 +411,7 @@ from pg_catalog.pg_constraint pgc
internal static DataTable GetDataSourceInformation()
{
DataSet ds = new DataSet();
- using (Stream xmlStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Npgsql.NpgsqlMetaData.xml"))
+ using (Stream xmlStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Npgsql.Npgsql.NpgsqlMetaData.xml"))
{
ds.ReadXml(xmlStream);
}
diff --git a/Npgsql/policy.2.0.Npgsql.config b/Npgsql/policy.2.0.Npgsql.config
new file mode 100644
index 0000000000..bc4a8f7e3d
--- /dev/null
+++ b/Npgsql/policy.2.0.Npgsql.config
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Npgsql/policyFileBuild.bat b/Npgsql/policyFileBuild.bat
new file mode 100644
index 0000000000..a95296b14c
--- /dev/null
+++ b/Npgsql/policyFileBuild.bat
@@ -0,0 +1,28 @@
+@if "%WindowsSdkDir%"=="" goto NO_WIN_SDK
+
+@setlocal
+
+@set net20out=bin\policies\net20
+@set net40out=bin\policies\net40
+
+@if not exist %net20out% mkdir %net20out%
+@if not exist %net40out% mkdir %net40out%
+
+@set aldir=%WindowsSdkDir_35%
+
+:: If dev env is VS2010, then another path should be used.
+@if "%aldir%"=="" set aldir=%WindowsSDKDir%\Bin
+
+@"%aldir%\al.exe" /nologo /link:policy.2.0.Npgsql.config /out:%net20out%\policy.2.0.Npgsql.dll /keyfile:Npgsql\Npgsql.snk
+@"%aldir%\NETFX 4.0 Tools\al.exe" /nologo /link:policy.2.0.Npgsql.config /out:%net40out%\policy.2.0.Npgsql.dll /keyfile:Npgsql\Npgsql.snk
+@goto :EOF
+
+:NO_WIN_SDK
+@echo ==========================================================
+@echo ERROR:
+@echo Please make sure that an environment variable WindowsSdkDir_35 is set.
+@echo This variable should point to the SDK dir which contains al.exe for clr 2.0.
+@echo You can set this variable with launching command shell via Windows SDK Comamnd
+@echo Prompt or Visual Studio Command Prompt.
+@echo ==========================================================
+@pause
\ No newline at end of file
diff --git a/tests/App.config b/tests/App.config
new file mode 100644
index 0000000000..77cb73eb24
--- /dev/null
+++ b/tests/App.config
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/CommandTests.cs b/tests/CommandTests.cs
index 7b66d35227..3ddc35fea8 100644
--- a/tests/CommandTests.cs
+++ b/tests/CommandTests.cs
@@ -3481,5 +3481,32 @@ public void DataTypeTests()
Assert.AreEqual(typeof(NpgsqlTimeTZ), result.GetType());
*/
}
+
+ [Test]
+ // Target NpgsqlCommand.AppendCommandReplacingParameterValues()'s handling of operator @@.
+ public void Operator_At_At_RewriteTest()
+ {
+ NpgsqlCommand cmd = new NpgsqlCommand("SELECT to_tsvector('fat cats ate rats') @@ to_tsquery('cat & rat')", Conn);
+
+ Assert.IsTrue((bool)cmd.ExecuteScalar());
+ }
+
+ [Test]
+ // Target NpgsqlCommand.AppendCommandReplacingParameterValues()'s handling of operator @>.
+ public void Operator_At_GT_RewriteTest()
+ {
+ NpgsqlCommand cmd = new NpgsqlCommand("SELECT 'cat'::tsquery @> 'cat & rat'::tsquery", Conn);
+
+ Assert.IsFalse((bool)cmd.ExecuteScalar());
+ }
+
+ [Test]
+ // Target NpgsqlCommand.AppendCommandReplacingParameterValues()'s handling of operator <@.
+ public void Operator_LT_At_RewriteTest()
+ {
+ NpgsqlCommand cmd = new NpgsqlCommand("SELECT 'cat'::tsquery <@ 'cat & rat'::tsquery", Conn);
+
+ Assert.IsTrue((bool)cmd.ExecuteScalar());
+ }
}
}
diff --git a/tests/EntityFrameworkTests.cs b/tests/EntityFrameworkTests.cs
new file mode 100644
index 0000000000..0cbd04851f
--- /dev/null
+++ b/tests/EntityFrameworkTests.cs
@@ -0,0 +1,167 @@
+using Npgsql;
+using NpgsqlTests;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Data.Entity;
+using System.Linq;
+using System.Text;
+
+namespace NpgsqlTests
+{
+ public class EntityFrameworkTests : TestBase
+ {
+ public EntityFrameworkTests(string backendVersion) : base(backendVersion) { }
+
+ protected override void SetUp()
+ {
+ base.SetUp();
+ // If this is the first (or only) test being run, the connection has already been opened
+ // in the fixture setup. Save the extra connecting time.
+ ExecuteNonQuery(@" -- Table: posts
+ -- DROP TABLE posts;
+
+ CREATE TABLE posts
+ (
+ id serial NOT NULL,
+ title character varying,
+ body character varying,
+ user_name character varying,
+ search_vector tsvector,
+ CONSTRAINT posts_pkey PRIMARY KEY (id)
+ )
+ WITH (
+ OIDS=FALSE
+ );
+
+ -- Index: posts_search_idx
+
+ -- DROP INDEX posts_search_idx;
+
+ CREATE INDEX posts_search_idx
+ ON posts USING gin (search_vector);
+
+ -- Trigger: posts_vector_update on posts
+
+ -- DROP TRIGGER posts_vector_update ON posts;
+
+ CREATE TRIGGER posts_vector_update
+ BEFORE INSERT OR UPDATE ON posts
+ FOR EACH ROW
+ EXECUTE PROCEDURE tsvector_update_trigger('search_vector', 'pg_catalog.english', 'title', 'body');
+ ");
+
+ ExecuteNonQuery("INSERT INTO posts (title, body, user_name) VALUES ('Postgres is awesome', '', 'Clark Kent')");
+ ExecuteNonQuery("INSERT INTO posts (title, body, user_name) VALUES ('How postgres is differente from MySQL', '', 'Lois Lane')");
+ ExecuteNonQuery("INSERT INTO posts (title, body, user_name) VALUES ('Tips for Mysql', '', 'Bruce Wayne')");
+ ExecuteNonQuery("INSERT INTO posts (title, body, user_name) VALUES ('SECRET', 'Postgres for the win', 'Dick Grayson')");
+ ExecuteNonQuery("INSERT INTO posts (title, body, user_name) VALUES ('Oracle acquires some other database', 'Mysql but no postgres' , 'Oliver Queen')");
+ ExecuteNonQuery("INSERT INTO posts (title, body, user_name) VALUES ('No Database', 'Nothing to see here', 'Kyle Ryner')");
+ }
+
+ protected override void TearDown()
+ {
+ ExecuteNonQuery("DROP TABLE posts");
+ base.TearDown();
+ }
+
+ [Test]
+ public void FullTextSearchSimpleTest()
+ {
+ var conn = Npgsql.NpgsqlFactory.Instance.CreateConnection();
+ conn.ConnectionString = ConnectionString;
+ using (var ctx = new DbContext(conn, true))
+ {
+ var query = @"select *
+ from posts
+ where search_vector @@ to_tsquery('english', @p0)
+ order by ts_rank_cd(search_vector, to_tsquery('english', @p0)) desc";
+ var p = "postgres";
+ var posts = ctx.Database.SqlQuery(query, p).ToList();
+
+ Assert.AreEqual(4, posts.Count);
+ }
+ }
+
+ [Test]
+ public void FullTextSearchAndTest()
+ {
+ var conn = Npgsql.NpgsqlFactory.Instance.CreateConnection();
+ conn.ConnectionString = ConnectionString;
+ using (var ctx = new DbContext(conn, true))
+ {
+ var query = @"select *
+ from posts
+ where search_vector @@ to_tsquery('english', @p0)
+ order by ts_rank_cd(search_vector, to_tsquery('english', @p0)) desc";
+ var p = "postgres & mysql";
+ var posts = ctx.Database.SqlQuery(query, p).ToList();
+
+ Assert.AreEqual(2, posts.Count);
+ }
+ }
+
+ [Test]
+ public void FullTextSearchOrTest()
+ {
+ var conn = Npgsql.NpgsqlFactory.Instance.CreateConnection();
+ conn.ConnectionString = ConnectionString;
+ using (var ctx = new DbContext(conn, true))
+ {
+ var query = @"select *
+ from posts
+ where search_vector @@ to_tsquery('english', @p0)
+ order by ts_rank_cd(search_vector, to_tsquery('english', @p0)) desc";
+ var p = "postgres | mysql";
+ var posts = ctx.Database.SqlQuery(query, p).ToList();
+
+ Assert.AreEqual(5, posts.Count);
+ }
+ }
+
+ [Test]
+ public void DbContextSqlQueryMapWithUnderscoreColumnNames()
+ {
+ var conn = Npgsql.NpgsqlFactory.Instance.CreateConnection();
+ conn.ConnectionString = ConnectionString;
+ using (var ctx = new TestDbContext(conn, true))
+ {
+ var query = @"select * from posts";
+ var posts = ctx.Database.SqlQuery(query).ToList();
+
+ Assert.AreEqual(6, posts.Count);
+ Assert.AreEqual("Clark Kent", posts.FirstOrDefault().UserName);
+ }
+ }
+ }
+
+ public class TestDbContext : DbContext
+ {
+ public TestDbContext(DbConnection existingConnection, bool contextOwnsConnection)
+ : base(existingConnection, contextOwnsConnection)
+ {
+ Database.SetInitializer(null);
+ }
+
+ public DbSet Posts { get; set; }
+
+ protected override void OnModelCreating(DbModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+ modelBuilder.Entity().ToTable("posts", "public");
+ modelBuilder.Entity().Property(c => c.Id).HasColumnName("id").IsOptional();
+ modelBuilder.Entity().Property(c => c.Title).HasColumnName("title").IsOptional();
+ modelBuilder.Entity().Property(c => c.Body).HasColumnName("body").IsOptional();
+ modelBuilder.Entity().Property(c => c.UserName).HasColumnName("user_name").IsOptional();
+ }
+ }
+
+ public class Post
+ {
+ public int Id { get; set; }
+ public string Title { get; set; }
+ public string Body { get; set; }
+ public string UserName { get; set; }
+ }
+}
diff --git a/tests/NpgsqlTests.csproj b/tests/NpgsqlTests.csproj
index 1b380a8632..37b50fc716 100644
--- a/tests/NpgsqlTests.csproj
+++ b/tests/NpgsqlTests.csproj
@@ -131,7 +131,17 @@
4
v4.5
+
+ false
+
+
+
+
+
+
+ ..\packages\EntityFramework.6.0.1\lib\net40\EntityFramework.dll
+
..\packages\NUnit.2.6.2\lib\nunit.framework.dll
@@ -170,11 +180,13 @@
+
+
Always
@@ -214,6 +226,10 @@
+
+ {3ec85cba-5b79-11e3-8104-0022198ab089}
+ Npgsql.EntityFramework
+
{9D13B739-62B1-4190-B386-7A9547304EB3}
Npgsql2012
diff --git a/tests/packages.config b/tests/packages.config
index 5c3ca54dd7..fbdacc1a3b 100644
--- a/tests/packages.config
+++ b/tests/packages.config
@@ -1,4 +1,5 @@
+
\ No newline at end of file