postgresql 내장 개발의addmonths 함수 실전

postgresql 내부 함수 구현 설명
pg 앞에서 실현된 Helloworld 입문은 사실상 하나의 함수를 실현한 것이지만 이 함수는 설명적 의미만 있고 함수 실현의 세부 사항도 상세하게 소개하지 않았다. 본고는postgresql 내부에서 어떻게 하나의 함수의 실현을 완성하는지 상세하게 소개하고자 한다.oracle에서addmonths 함수는 현재 시간에 근거하여 몇 개월을 증가하거나 줄일 수 있으며,postgresql에는 이런 함수가 없습니다.addmonths는 다음postgresql의 함수 증가 방법을 소개하는 예가 있다.C 언어에서 하나의 함수를 실현하는 것은 매우 간단하다. 함수 주체를 실현하기만 하면 된다. 다른 파일에서 사용하면include 헤더 파일에서 명성을 얻으면 된다. 이 두 단계는postgresql 핵에서도 자연히 있어야 하지만 동작을 추가하여 pg 에 등록해야 한다.proc 시스템 테이블에서 pgproc는postgresql의 함수의 시스템 표입니다.우선 함수를 실현한다. 시간과 관련된 함수이기 때문에 src/backend/utils/adt/date에 넣을 수 있다.c중,addmonths_date 및 addmonths_timestamp, 헤더 파일 src/include/utils/date.h 명성 증가, 코드는 다음과 같다.
Datum
add_months_date(PG_FUNCTION_ARGS)
{
  DateADT    date = PG_GETARG_DATEADT(0);
  int    added_mon = PG_GETARG_INT32(1);
  struct pg_tm  tt;
  struct pg_tm * tm = &tt;
  bool   isLastDay = false;
  int    LastDay;
  int    year;
  int    mon;
  int    day;
  DateADT    result = 0;

  /* check range */
  if (DATE_NOT_FINITE(date))
  {
    PG_RETURN_DATEADT(date);
  }
  j2date((date + POSTGRES_EPOCH_JDATE), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
  year = tm->tm_year;
  mon = tm->tm_mon;
  day = tm->tm_mday;

  isLastDay = (day == day_tab[isleap(year)][mon - 1]);
  mon += added_mon;
  if (mon > 12)
  {
    year += ((mon - 1) / 12);
    mon = (((mon - 1) % 12) + 1);
  }
  else if (mon < 1)
  {
    year += ((mon / 12) - 1);
    mon = ((mon % 12) + 12);
  }

  LastDay = day_tab[isleap(year)][mon - 1];
  day = isLastDay ? LastDay : ((day > LastDay) ? LastDay : day);

  if (!IS_VALID_JULIAN(year, mon, day))
  {
    ereport(ERROR,
    (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
    errmsg("date out of range")));
  }
  result = (DateADT)(date2j(year, mon, day) - POSTGRES_EPOCH_JDATE);

  PG_RETURN_DATEADT(result);
}

Datum
add_months_timestamp(PG_FUNCTION_ARGS)
{
  Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
  int    added_mon = PG_GETARG_INT32(1);
  struct pg_tm  tt;
  struct pg_tm * tm = &tt;
  bool   isLastDay = FALSE;
  int    LastDay;
  int    year;
  int    mon;
  int    day;
  int    tz;

  fsec_t     fsec;
  Timestamp result = 0;


  if (TIMESTAMP_NOT_FINITE(timestamp))
  {
    PG_RETURN_TIMESTAMP(timestamp);
  }

  if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
  {
    ereport(ERROR,
    (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
    errmsg("timestamp out of range")));
  }

  year = tm->tm_year;
  mon = tm->tm_mon;
  day = tm->tm_mday;

  isLastDay = (day == day_tab[isleap(year)][mon - 1]);
  mon += added_mon;
  if (mon > 12)
  {
    year += ((mon - 1) / 12);
    mon = (((mon - 1) % 12) + 1);
  }
  else if (mon < 1)
  {
    year += ((mon / 12) - 1);
    mon = ((mon % 12) + 12);
  }

  LastDay = day_tab[isleap(year)][mon - 1];
  day = isLastDay ? LastDay : ((day > LastDay) ? LastDay : day);

  tm->tm_year = year;
  tm->tm_mon = mon;
  tm->tm_mday = day;

  if (tm2timestamp(tm, fsec, NULL, &result) != 0)
  {
    ereport(ERROR,
    (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
    errmsg("timestamp out of range")));
  }

  PG_RETURN_TIMESTAMP(result);
}  

src/include/utils/date.h
extern Datum add_months_date(PG_FUNCTION_ARGS);
extern Datum add_months_timestamp(PG_FUNCTION_ARGS);

여기는 아니야ddmonths 함수 내부 논리가 깊고 관심 있는 것은oracle 이 함수의 표현을 연구할 수 있다. 대조적으로 함수체 실현을 완성한 데 중점을 두고 pg 에 다시 등록하기만 하면proc에서 새로운 증가 함수를 실현할 수 있습니다.어떻게 등록합니까?src/include/catalog/pgproc.h 새 코드는 다음과 같습니다.
DATA(insert OID = 9998 (add_months  PGNSP PGUID 12 1 0 0 0 f f f f t f i f 2 0 1082 "1082 23" _null_ _null_ _null_ _null_ _null_ add_months_date _null_ _null_ _null_));
DATA(insert OID = 9997 (add_months  PGNSP PGUID 12 1 0 0 0 f f f f t f i f 2 0 1114 "1114 23" _null_ _null_ _null_ _null_ _null_ add_months_timestamp _null_ _null_ _null_));
DATA(insert OID = 9996 (add_months  PGNSP PGUID 14 1 0 0 0 f f f f t f i f 2 0 1114 "1184 23" _null_ _null_ _null_ _null_ _null_ "select add_months($1::timestamp,$2)" _null_ _null_ _null_));

OK, 추가해야 할 코드가 모두 완성되고, 컴파일되고, 설치되고, 라이브러리를 다시 초기화하고, 다음 테스트를 실행합니다.
postgres=# \d a
                 Table "public.a"
 Column |            Type             | Modifiers
--------+-----------------------------+-----------
 a      | date                        |
 b      | timestamp without time zone |


postgres=# select *from a;
     a      |             b
------------+----------------------------
 2016-12-04 | 2016-12-04 22:17:28.447829
 2016-02-28 | 2016-02-29 00:00:00
(2 rows)


postgres=# select a,add_months(a,3),b,add_months(b,4) from a;
     a      | add_months |             b              |         add_months
------------+------------+----------------------------+----------------------------
 2016-12-04 | 2017-03-04 | 2016-12-04 22:17:28.447829 | 2017-04-05 06:17:28.447829
 2016-02-28 | 2016-05-28 | 2016-02-29 00:00:00        | 2016-06-30 08:00:00
(2 rows)

add_months의 기능이 정상적입니다. 이로써 내부 함수가 증가했습니다. 다음에 질문하는 방식으로 다음 가능한 의문들을 상세하게 소개하겠습니다.첫 번째 문제, 왜 두 개의 함수를 추가해야 하는지, 하나만add 이 아니다.months요?사실 모두 세 개가 늘었다.이 세 외부 호출은 모두addmonths 함수, 매개 변수에 따라 재부팅, pgproc.h 등록할 때 이 매개 변수를 설명합니다.(뒤에 상세한 설명이 있다) 두 번째 질문, 등록은 무슨 뜻입니까?템플릿 라이브러리에 기록을 삽입해서 라이브러리를 만들면 바로 있습니다.시스템 테이블에 대해 말하자면, 헤더 파일에 이 시스템 테이블에 대한 상세한 정의 설명이 있습니다. 이 헤더 파일 아래에 형식에 따라 기록을 미리 설정할 수 있습니다. 이 기록은 initdb에 있을 때 템플릿 라이브러리에 대응하는 시스템 테이블에 삽입됩니다.구체적으로 말하면, 이러한 미리 설정된 기록은 컴파일하는 과정에서perl 스크립트에postgres로 변환됩니다.bki에서 이 bki 파일은 디렉터리를 설치하는share 폴더에 있습니다. initdb에서 이 bki를 불러오고 하나의 ql 실행으로 해석하여 시스템 테이블을 만들고 초기 기록을 삽입하여 초기 정보를 준비합니다.관심 있는 분들은 initdb 모듈에서bootstraptemplate1 함수, 모듈을 초기화할 때 첫 번째는postgres를 불러오는 것입니다.bki 파일 번역 해석 실행.세 번째 문제, pgproc.h에 삽입된 기록은 어떤 의미입니까?첫 번째 행동 예로는 DATA(insert OID = 9998(add months PGNSP PGUID 12 1 0 0 f f f f t f i f 2 0 1082 "1082 23"null null null null null add months date null null null);
  • 9998 – OID는 커널에 사용되지 않은 OID를 사용하면 됩니다(src/include/catalog 아래 unused oids, 사용되지 않은 oid를 표시할 수 있음)postgres 내부에 1W 이상의 oid를 시스템에 미리 남겨 두고 없는 것을 선택하면 됩니다. 사용할 수 있는 것을 모르면\src\include\catalog\아래에 스크립트 파일 unusedoids, 실행하면 어떤 oid가 사용할 수 있는지 찾을 수 있지만, 이것은 linux 스크립트라면, linux에서 실행해야 합니다.
  • add_months – 함수 이름, 우리가 SQL에서 사용하는 외부 함수, 그러나 내부 기능 함수에 대응하는 함수는 하나가 아닐 수 있음
  • PGNSP – 함수에 속하는 이름 공간의 OID, PGNSP 즉 pgcatalog(oid=11), 내장 함수에 이 값을 추가하여 고정
  • PGUID - 함수의 소유자 OID, PGUID 및 initdb 시 사용자 지정(oid=10), 내장 함수에 이 값을 추가하여 고정
  • 12 - 언어 또는 이 함수의 호출 인터페이스를 실현하고 내장 함수는 12(internal), SQL은 14를 사용한다. 예를 들어 세 번째 함수 실현
  • 1 – 예상된 집행 대가, 만약 proretset이 사실이라면 이것은 줄마다 되돌아오는 대가
  • 0 - 추정된 결과 줄 수량(proretset이 가짜라면 이 값은 0)
  • 0 - 변수 그룹 매개 변수의 원소의 데이터 형식, 함수에 변수 변수가 없으면 0
  • 0 - 이 함수를 호출할 때 이 열에서 지정한 함수를 간략화할 수 있음
  • f – 함수가 하나의 집합 함수인지 여부
  • f – 함수가 하나의 창 함수인지 여부
  • f – 함수는 보안 정의자(즉, "setuid"함수)
  • f - 이 함수는 부작용이 없다.반환 값을 제외하고는 매개 변수에 대한 정보가 전파되지 않았다.매개 변수 값을 기반으로 한 오류 정보를 던질 함수는 유출 검증이 아닙니다.
  • t - 임의의 호출 함수가 비어 있을 때 함수가 비어 있는지 여부입니다.그런 상황에서 함수는 실제로는 전혀 호출되지 않는다.비"strict"함수는 빈 값 입력을 처리할 준비가 되어 있어야 합니다.
  • f – 함수가 하나의 집합(즉, 데이터 형식의 여러 값을 지정하는 것)을 되돌려줄지 여부
  • i –provolatile는 함수가 입력 매개 변수에만 의존하는지 외부 요소에 영향을 받는지 설명한다.값 i는 같은 입력에 대해 항상 같은 결과를 출력하는 변하지 않는 함수를 나타낸다.값 s는 '안정적' 함수를 나타냅니다. 그 결과는 (고정된 입력에 대해서는) 한 번의 스캐닝에서 변하지 않습니다.값 v는'불안정한'함수를 나타낸다. 그 결과는 언제든지 바뀔 수 있다(v 페이지로 함수를 표시하는 것은 부작용이 있기 때문에 호출을 최적화할 수 없다)
  • f – 함수 유형(이 항목은 우리가 개발하는 과정에서 추가된 필드로 함수 유형을 나타내고 f 대표 함수, p 대표 프로세스, t 대표 트리거),postgresql 핵에 이 인자가 없으면 무시할 수 있음
  • 2 - 뒤에 108223 2개 매개변수
  • 에 해당하는 매개변수의 개수를 입력합니다.
  • 0 - 기본값이 있는 매개변수 개수
  • 1082 – 반환 값의 데이터 유형
  • "108223"- 함수 파라미터의 데이터 형식의 수조로 입력 파라미터(INOUT와VARIADIC 파라미터 포함)만 포함하기 때문에 함수의 호출 특징 재부팅 함수도 이 차이점을 나타낸다. 예를 들어addmonths 함수, 만약에 여러 개가 있다면 매개 변수가 틀림없이 다르다. 이 차이는 수량이 다를 수도 있고 유형이 다를 수도 있다. 108223은 바로 유형을 대표한다. 다음과 같다.
    postgres=# select oid,typname from pg_type where oid in (1082,23,1114,1184) ;
    oid  |   typname   
    ------+-------------
     23 | int4
    1082 | date
    1114 | timestamp
    1184 | timestamptz
    (4 rows)
  • _null_–모든 매개변수(OUT 및 INOUT 매개변수 포함)를 포함하는 함수 매개변수의 데이터 유형 배열그러나 모든 매개변수가 IN 매개변수인 경우 이 필드는 비어 있습니다.아래 첨자는 1에서 시작하지만, 역사적 원인으로 인해proargtypes의 아래 첨자는 0에서 시작합니다
  • _null_–함수 매개 변수의 패턴 그룹입니다.인코딩: i는 IN 매개변수, o는 OUT 매개변수, b는 INOUT 매개변수, v는 VARIADIC 매개변수, t는 TABLE 매개변수입니다.모든 매개변수가 IN 매개변수인 경우 이 필드는 비어 있습니다.이 아래 표시는 proallargtypes가 아니라proargtypes의 위치에 대응하는 것을 주의하십시오
  • _null_–함수 매개 변수의 이름의 수조입니다.이름이 없는 매개 변수는 그룹에서 빈 문자열로 설정됩니다.이름이 있는 인자가 없으면 이 필드는 비어 있습니다.이 아래 표시는 proallargtypes가 아니라proargtypes의 위치에 대응하는 것을 주의하십시오
  • _null_–기본값의 표현식 트리 ((nodeToString() 의 표현 방식에 따라).이것은 pronargdefaults 요소의 목록입니다. 마지막 N 개의 input 인자 (즉 마지막 N 개의 proargtypes 위치) 에 대응합니다.기본값이 있는 인자가 없으면 이 필드는 비어 있습니다
  • _null_–변환을 적용하기 위한 데이터 유형 OID
  • add_months_date - 함수 처리자가 이 함수를 어떻게 호출합니까?이것은 해석형 언어에 대한 실제 원본, 기호 링크, 파일 이름 또는 그 어떠한 것일 수도 있습니다. 이것은 언어/호출 습관을 실현하는 데 달려 있습니다. 간단하게 말하면 add 입니다.months가 진정으로 운행하는 것, 첫 번째는addmonths_date, 두 번째는ddmonths_timestamp 함수, 세 번째는 sql 문장select addmonths( 1::timestamp, 2).비록 우리가 sql 문장을 쓸 때 관련 유형에 너무 관심을 가지는 것은 아니지만, 실행할 때 틀림없이 어떤 함수를 정확하게 조정하여 기능을 완성할 것이다.첫 번째 처리는addmonths(date, int), 두 번째 처리는addmonths(timestamp, int), 세 번째 처리는addmonths (timestamptz, int) 를 통해 sql 함수를 다시 불러옵니다.
  • _null_–함수를 어떻게 호출하는지에 대한 추가 정보그 해석은 언어와 관련이 있다
  • _null_–함수 런타임 구성 변수에 대한 로컬 설정 값
  • _null_–액세스 권한
  • 네 번째 질문, Datum, PGFUNCTION_ARGS,PG_GETARG_DATEADT,PG_RETURN_DATEADT, 각각 무엇입니까?Datum은 일반적인 유형입니다. 어떤 종류의 값을 저장하고 어떤 값을 수신자가 스스로 해석하는지.PG_FUNCTION_ARGS는 매개 변수의 매크로로 함수에 대한 정보가 들어 있다.PG_GETARG_DATEADT는 앞에 있는 매개변수의 해당 위치에 대한 매개변수 값입니다.PG_RETURN_DATEADT는 값을 대응하는 형식으로 되돌려주고 나머지 바이트를 정리합니다.이 상세한 것들은 코드를 볼 수 있는데, 특히 PGFUNCTION_ARGS의 정의 사용법은 함수를 이해하는 데 큰 도움이 된다.오케이, 이번엔 여기까지 여러분께 도움이 되었으면 좋겠습니다.

    좋은 웹페이지 즐겨찾기