Microsoft MVP성태의 닷넷 이야기
개발 환경 구성: 90. 닷넷에서 접근해보는 PostgreSQL DB [링크 복사], [링크+제목 복사],
조회: 90393
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 4개 있습니다.)
닷넷에서 접근해보는 PostgreSQL DB

드디어, "PostgreSQL" 데이터베이스를 사용하고 있는 예비 고객사에서 ^^ "제니퍼 닷넷 APM 솔루션"에서 PostgreSQL DB 접근에 대한 모니터링이 가능한지 문의가 들어왔습니다. 그래서 이렇게 PostgreSQL을 처음 설치하고 만져보았는데요. 오호~~~ "관계형 데이터베이스"들이 SQL 쿼리를 지원하고 닷넷의 경우에는 .NET Data Provider를 위한 인터페이스들이 강제되어 있다 보니 기본적인 사용법이 그다지 어렵지 않더군요.

물론, 제가 바닥부터 ^^ PostgreSQL 접근을 알아본 것은 아니고 아래의 글에서부터 시작을 했습니다.

Using PostgreSQL in your C# (.NET) application (An introduction)
; http://www.codeproject.com/KB/database/afppostgresqlintro.aspx

당연히 PostgreSQL 데이터베이스를 서버에 설치부터 해야할텐데, 다음의 경로에서 윈도우 버전의 설치 파일을 다운로드 할 수 있습니다.

Download PostgreSQL 
; http://www.enterprisedb.com/products/pgdownload.do#windows

제 경우에는 x64 운영체제에 설치하기 때문에 "Win x86-64" 아이콘을 눌러서 다운로드 받은 후 설치를 했습니다. 다행히 설치는 무척 간단합니다. ^^ 여느 DB처럼, superuser를 위한 암호를 묻고, 기본 접속 포트는 5432로 설치를 마친 후 SQL 서버의 "Management Studio"와 같은 도구와 유사한 "시작" / "PostgreSQL 9.0" / "pgAdmin III" 프로그램을 실행해서 GUI 환경에서 관리를 해줄 수 있습니다.

관리방식도 꽤 직관적입니다. 누구나 한번쯤 아래와 같이 "오른쪽 마우스 버튼"을 눌러서 "Connect"를 실행해 볼 것이고,

postgresql_dotnet_1.png

설치 시 입력했던 관리자 암호를 입력한 후,

postgresql_dotnet_2.png

연결 문자열에 사용할 테스트용 사용자 계정(ID: testuser)을 아래와 같이 "Login Roles" 노드에서 생성할 수 있습니다.

postgresql_dotnet_3.png

"Database" 노드에서는 새로운 DB를 생성(Name: testdb, Owner: testuser)해 줄 수 있고,

postgresql_dotnet_4.png

생성된 "testdb" 하위 노드의 "Schemas / public / Tables" 에서 테이블을 생성할 수 있습니다.

postgresql_dotnet_5.png

Name: TestTable
Owner: testuser


"New Table..." 대화창의 "Columns" 탭에서 "Add" 버튼을 눌러 다음과 같이 "칼럼"을 추가해 주는 것으로 테스트 환경 구축은 거의 완료가 됩니다.

postgresql_dotnet_6.png

tid: bigserial NOT NULL
name: character varying(150) NOT NULL
age: integer NOT NULL


Primary Key는 "TestTable" 노드를 마우스 오른쪽 버튼 클릭해서 "New Object" / "New Primary Key" 메뉴 선택으로 지정해 줄 수 있습니다. 그 외에 SQL Server와의 변환 체계는 다음의 글을 참고하시면 되겠습니다.

Conversion of Microsoft SQL/ASP applications to PostgreSQL
; http://wiki.postgresql.org/images/e/e4/5.pdf




설치 및 테스트용 DB 구성을 정상적으로 서버에서 마쳤으면, 이제 개발자 PC에 PostgreSQL 서버를 위한 ".NET Data Provider"를 다운로드 받아야 하는데, 다음의 경로에서 C#으로 구현된 오픈소스 "Npgsql"을 구할 수 있습니다.

Npgsql - a .Net data provider for Postgresql
; http://pgfoundry.org/projects/npgsql

다운로드 Npgsql
; http://pgfoundry.org/frs/?group_id=1000140&release_id=958

Microsoft .NET Framework을 위해서 3가지 링크가 제공되는데요.

  1. Npgsql2.0.11-bin-ms.net.zip 464 KB 1,540 Any .zip
  2. Npgsql2.0.11-bin-ms.net3.5sp1.zip 511 KB 1,393 Any .zip
  3. Npgsql2.0.11-bin-ms.net4.0.zip

음... 2번과 3번의 차이는 쉽게 알겠는데 1번은 왜 있는지 잘 모르겠군요. 그냥 무시하고 ^^ 2번과 3번 중에서 원하는 것을 받아 압축을 해제한 후, 다음의 2개 파일만 여러분들의 웹 사이트 프로젝트에 복사해서 참조를 하시면 됩니다. (또는 GAC에 등록을 해주거나.)

  • Mono.Security.dll
  • Npgsql.dll

이렇게 마치고, 처음 연결문자열을 다음과 같이 설정하고 접속을 했는데,

string connectionString = "Server=testpc;Port=5432;User Id=testuser;Password=testuser;Database=testdb";
using (NpgsqlConnection connection = new NpgsqlConnection())
{
    connection.ConnectionString = connectionString;
    connection.Open();

지금까지 너무 쉽긴 했지요. ^^; 아래와 같이 오류가 발생합니다.

System.IO.IOException occurred
  Message=Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
  Source=System
  StackTrace:
       at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
       at System.IO.BufferedStream.ReadByte()
       at Npgsql.NpgsqlState.<ProcessBackendResponses_Ver_3>d__a.MoveNext() in C:\...\NpgsqlState.cs:line 665
  InnerException: System.Net.Sockets.SocketException
       Message=An existing connection was forcibly closed by the remote host
       Source=System
       ErrorCode=10054
       NativeErrorCode=10054
       StackTrace:
            at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
            at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
       InnerException: 

기본값이 SSL 연결만 허용하기 때문인데, "pgAdmin III" 도구에서 "Tools" / "Server Configuration" / "postgresql.conf" 메뉴를 선택하고 아래와 같이 "SSL off" 옵션을 허용시켜 주면 됩니다.

postgresql_dotnet_7.png

아니... ^^; 그래도 다음과 같이 오류가 발생하더군요.

Npgsql.NpgsqlException was unhandled by user code
  Message=FATAL: 28000: no pg_hba.conf entry for host "192.168.0.132", user "testuser", database "testdb", SSL off
  Source=Npgsql
  ErrorCode=-2147467259
  BaseMessage=no pg_hba.conf entry for host "192.168.0.132", user "testuser", database "testdb", SSL off
  Code=28000
  Detail=""
  ErrorSql=""
  File=.\src\backend\libpq\auth.c
  Hint=""
  Line=464
  Position=""
  Routine=ClientAuthentication
  Severity=FATAL
  Where=""
  StackTrace:
       at Npgsql.NpgsqlState.<ProcessBackendResponses_Ver_3>d__a.MoveNext() in C:\...\NpgsqlState.cs:line 686
       at Npgsql.NpgsqlState.IterateThroughAllResponses(IEnumerable`1 ienum) in C:\...\NpgsqlState.cs:line 319
       at Npgsql.NpgsqlState.ProcessBackendResponses(NpgsqlConnector context) in C:\...\NpgsqlState.cs:line 314
       at Npgsql.NpgsqlConnectedState.Startup(NpgsqlConnector context) in C:\...\NpgsqlConnectedState.cs:line 52
       at Npgsql.NpgsqlConnector.Open() in C:\...\NpgsqlConnector.cs:line 656
       at Npgsql.NpgsqlConnectorPool.GetPooledConnector(NpgsqlConnection Connection) in C:\...\NpgsqlConnectorPool.cs:line 423
       at Npgsql.NpgsqlConnectorPool.RequestPooledConnectorInternal(NpgsqlConnection Connection) in C:\...\NpgsqlConnectorPool.cs:line 226
       at Npgsql.NpgsqlConnectorPool.RequestPooledConnector(NpgsqlConnection Connection) in C:\...\NpgsqlConnectorPool.cs:line 178
       at Npgsql.NpgsqlConnectorPool.RequestConnector(NpgsqlConnection Connection) in C:\...\NpgsqlConnectorPool.cs:line 158
       at Npgsql.NpgsqlConnection.Open() in C:\...\NpgsqlConnection.cs:line 543
       at Jennifer40.WebSiteTest.postgreSQLTest.Page_Load(Object sender, EventArgs e) in D:\workshop\Jennifer\Sources\Agent\UnitTest\Jennifer40.WebSiteTest\postgreSQLTest.aspx.cs:line 23
       at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
       at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
       at System.Web.UI.Control.OnLoad(EventArgs e)
       at System.Web.UI.Control.LoadRecursive()
       at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
  InnerException: 

음... 용서해줄 수 있습니다. 기본적인 "Lockdown" 보안 정책은 충분히 이해해 줄 수 있거든요. ^^

검색을 해보니까, PostgreSQL 서버는 접속하려는 측의 IP를 별도로 등록해 주거나, 모든 IP로부터의 접속을 허용한다는 식의 설정을 명시적으로 해주어야 한다고 나옵니다.

그래서, "pgAdmin III" 도구에서 "Tools" / "Server Configuration" / "pg_hba.conf" 메뉴를 선택하고 다음과 같이 NpgsqlConnection 개체를 사용하는 클라이언트 측의 컴퓨터 IP를 등록해 주었습니다.

postgresql_dotnet_8.png

host testdb all 192.168.0.132/32 md5


보시는 것처럼 CIDR 방식으로 지정하는 것이 가능하기 때문에, 일정한 IP 대역을 선택하는 것도 가능합니다. 예를 들어, 다음과 같이 해주면 192.168.0.*의 모든 컴퓨터로부터 접속을 허용합니다.

host     testdb  all     192.168.0.0/24  md5

설정을 변경한 후에는 "서비스 관리자"에서 "postgresql-x64-9.0" NT 서비스를 재시작 해주어야 합니다.

기타의 보다 자세한 사항은 다음의 문서를 보시면 도움이 될 것입니다. ^^

PostgreSQL 8.1.22 Documentation
- Chapter 20. Client Authentication
; http://www.postgresql.org/docs/8.1/interactive/client-authentication.html

물론, 이렇게 적용하고 난 후에는 정상적으로 NpgsqlConnection 연결이 되었고 그 외의 NpgsqlCommand, NpgsqlParameter, NpgsqlDataReader 개체는 아래의 예제 코드에서 보는 것처럼, 예의 "@MyParam"이 아닌 ":MyParam"과 같이 파라미터를 지원하는 차이만을 제외하고는 늘 해오던 수준에서 해결이 되었습니다.

using (NpgsqlConnection connection = new NpgsqlConnection())
{
    connection.ConnectionString = connectionString;
    connection.Open();

    NpgsqlCommand command = new NpgsqlCommand();

    command.Connection = connection;
    command.CommandText = "SELECT * FROM TestTable WHERE :MyParam is not null";
    command.Parameters.Add(new NpgsqlParameter("MyParam", "test"));

    NpgsqlDataReader reader = command.ExecuteReader();
    if (reader != null)
    {
        while (reader.Read())
        {
        }

        reader.Close();
    }
}




한가지, 시행착오를 더 설명드려야겠군요. ^^

"pgAdmin III" 도구에서 테이블 명을 "TestTable"과 같이 대소문자를 섞어서 지정한 경우 다음과 같은 쿼리를 NpgsqlCommand 개체에 지정해서 실행하면 오류가 발생합니다.

SELECT * FROM TestTable

오류 내용은 다음과 같습니다.

Npgsql.NpgsqlException was unhandled by user code
  Message=ERROR: 42P01: relation "testtable" does not exist
  Source=Npgsql
  ErrorCode=-2147467259
  BaseMessage=relation "testtable" does not exist
  Code=42P01
  Detail=""
  ErrorSql=SELECT * FROM TestTable WHERE ((E'test')) is not null AND 1 = 1 AND 'A' = 'A'
  File=.\src\backend\parser\parse_relation.c
  Hint=""
  Line=857
  Position=15
  Routine=parserOpenTable
  Severity=ERROR
  Where=""
  StackTrace:
  ...[생략]...
       at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
  InnerException: 

해답은 아래에서 찾아보니 나오는데요.

Re: Database, Table Names are resulting in lowercase
; http://archives.postgresql.org/pgsql-admin/2004-06/msg00324.php

명시적으로 "쿼리문"에 포함된 테이블명을 대소문자로 인식시키려면 다음과 같이 쿼리를 바꿔주어야 합니다.

SELECT * FROM "TestTable"

C#의 경우, 아래와 같이 설정됩니다.

command.CommandText = "SELECT * FROM \"TestTable\"";

아니면, 그냥 속편하게 "pgAdmin III" 도구에서 테이블 생성할 때 소문자로만 이름을 줘도 되겠고.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/28/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2012-02-14 11시37분
위의 글에서 "Npgsql2.0.11-bin-ms.net.zip" 파일의 용도를 모르겠다고 했는데... 차이점을 알았습니다. ^^ 그 파일은 순수하게 .NET 2.0 Framework 만 설치된 경우에 사용할 수 있습니다. "2.Npgsql2.0.11-bin-ms.net3.5sp1.zip"에 포함된 Npgsql.dll 은 .NET 3.5 에서만 배포되는 System.Data.Entity.dll 어셈블리를 참조하고 있다는 차이가 있습니다.
정성태
2015-12-01 12시33분
[gwise] 테이블 대소문자 관련 내용이 "PostgreSQL for Data Architects" 책의 210 page에도 나와 있어서 덧글 남기고 갑니다.
테이블 생성할때 대문자나 소문자 하나로 통일하고 쿼리로 사용할때는 대소문자 섞어서 사용해도 됩니다.(이게 좀 귀찮긴 하네요)
닷넷 관련 내용 잘 보고 있습니다.
[guest]
2015-12-01 01시01분
그러게요. 살짝 귀찮긴 하지만 저 정도는 감수해도 무방할 듯 합니다. ^^
정성태
2015-12-01 04시08분
[gwise] GUI툴로 테이블 만들어 내는 쿼리를 보니 대소문자 구분할때는 위에 말씀하신 바와 같이 쌍따옴표가 있습니다.
그러나 쿼리에서 직접 쌍따옴표 없이 대소문자 같이 있는 스크립트로 테이블을 만드니 전부 소문자로 생성이 되네요.
결국 원인은 테이블 생성할때의 차이때문인거 같습니다.

그리고 아래와 같이 두개 테이블 쿼리에서 생성하면 두개 다 생성이 됩니다.
CREATE TABLE Mytb (Id integer ) <- 얘는 실제로는 전부 소문자로 생성됨.
CREATE TABLE "Mytb" ("Id" integer ) <- GUI에서 테이블 생성하면 이렇게 query됨.

Postgresql 테이블이나 컬럼 생성할때 맘편하게 소문자로 하는게 정신건강에 좋을거 같습니다. ^^
[guest]
2021-08-06 09시37분
정성태

... 31  32  33  34  35  [36]  37  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12748정성태8/2/20216605개발 환경 구성: 589. Azure Active Directory - tenant의 관리자(admin) 계정 로그인 방법
12747정성태8/1/20217181오류 유형: 748. 오류 기록 - MICROSOFT GRAPH – HOW TO IMPLEMENT IAUTHENTICATIONPROVIDER파일 다운로드1
12746정성태7/31/20219219개발 환경 구성: 588. 네트워크 장비 환경을 시뮬레이션하는 Packet Tracer 프로그램 소개
12745정성태7/31/20217057개발 환경 구성: 587. Azure Active Directory - tenant의 관리자 계정 로그인 방법
12744정성태7/30/20217676개발 환경 구성: 586. Azure Active Directory에 연결된 App 목록을 확인하는 방법?
12743정성태7/30/20218390.NET Framework: 1083. Azure Active Directory - 외부 Token Cache 저장소를 사용하는 방법파일 다운로드1
12742정성태7/30/20217585개발 환경 구성: 585. Azure AD 인증을 위한 사용자 인증 유형
12741정성태7/29/20218803.NET Framework: 1082. Azure Active Directory - Microsoft Graph API 호출 방법파일 다운로드1
12740정성태7/29/20217438오류 유형: 747. SharePoint - InvalidOperationException 0x80131509
12739정성태7/28/20217402오류 유형: 746. Azure Active Directory - IDW10106: The 'ClientId' option must be provided.
12738정성태7/28/20218032오류 유형: 745. Azure Active Directory - Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).
12737정성태7/28/20216965오류 유형: 744. Azure Active Directory - The resource principal named api://...[client_id]... was not found in the tenant
12736정성태7/28/20217526오류 유형: 743. Active Azure Directory에서 "API permissions"의 권한 설정이 "Not granted for ..."로 나오는 문제
12735정성태7/27/20218072.NET Framework: 1081. C# - Azure AD 인증을 지원하는 데스크톱 애플리케이션 예제(Windows Forms) [2]파일 다운로드1
12734정성태7/26/202124081스크립트: 20. 특정 단어로 시작하거나/끝나는 문자열을 포함/제외하는 정규 표현식 - Look-around
12733정성태7/23/202111350.NET Framework: 1081. Self-Contained/SingleFile 유형의 .NET Core/5+ 실행 파일을 임베딩한다면? [1]파일 다운로드2
12732정성태7/23/20216630오류 유형: 742. SharePoint - The super user account utilized by the cache is not configured.
12731정성태7/23/20217835개발 환경 구성: 584. Add Internal URLs 화면에서 "Save" 버튼이 비활성화 된 경우
12730정성태7/23/20219354개발 환경 구성: 583. Visual Studio Code - Go 코드에서 입력을 받는 경우
12729정성태7/22/20218315.NET Framework: 1080. xUnit 단위 테스트에 메서드/클래스 수준의 문맥 제공 - Fixture
12728정성태7/22/20217721.NET Framework: 1079. MSTestv2 단위 테스트에 메서드/클래스/어셈블리 수준의 문맥 제공
12727정성태7/21/20218774.NET Framework: 1078. C# 단위 테스트 - MSTestv2/NUnit의 Assert.Inconclusive 사용법(?) [1]
12726정성태7/21/20218607VS.NET IDE: 169. 비주얼 스튜디오 - 단위 테스트 선택 시 MSTestv2 외의 xUnit, NUnit 사용법 [1]
12725정성태7/21/20217261오류 유형: 741. Failed to find the "go" binary in either GOROOT() or PATH
12724정성태7/21/202110036개발 환경 구성: 582. 윈도우 환경에서 Visual Studio Code + Go (Zip) 개발 환경 [1]
12723정성태7/21/20217656오류 유형: 740. SharePoint - Alternate access mappings have not been configured 경고
... 31  32  33  34  35  [36]  37  38  39  40  41  42  43  44  45  ...