고도화: 전 모듈 dbio 25→40 · batch 17→22 세분화 (+432본, 전부 고유)

- 11개 모듈 각 dbio +15(→40), batch +5(→22) 새 고유 ECPG 프로그램 + 카피북 헤더
  (window/HAVING/JOIN/FILTER/NOT EXISTS/upsert/correlated/INSERT..SELECT 등 구조 다양)
- 전 코퍼스 .pgc 1004/1004 정규화-고유 (dup 그룹 0)
- 통합 검증: 12서버 runok, 333 서비스, 242 배치 바이너리, 매입체인 XA 커밋, prepared_xacts=0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
forge-bot 2026-07-19 14:37:10 +00:00
parent 05ad8a3064
commit fc16ea64c8
422 changed files with 8717 additions and 0 deletions

View file

@ -0,0 +1,35 @@
/*
* mm_bin_dedup_batch.pgc - mm 비활성 BIN 정리 배치 (DELETE range + XA).
*
* 지정 카드브랜드의 비활성(status<>'ACTIVE') BIN 행을 한 문장으로 삭제하고
* 삭제행수를 sqlca.sqlerrd[2] 로 확인한다. ONE global XA transaction.
* host 변수는 전용 카피북 mm_bin_dedup_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_bin_dedup_batch [CARD_BRAND]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *brand = (argc > 1) ? argv[1] : "LOCAL";
EXEC SQL INCLUDE mm_bin_dedup_cpy;
strncpy(d_brand, brand, sizeof(d_brand)-1); d_brand[sizeof(d_brand)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DELETE FROM mm_bin WHERE card_brand = :d_brand AND status <> 'ACTIVE';
if (sqlca.sqlcode < 0) { fprintf(stderr, "DELETE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
d_deleted = (long) sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_bin_dedup_batch COMMIT: brand=%s 삭제=%ld건\n", brand, d_deleted);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* mm_bin_dedup_cpy.h - mm_bin_dedup_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char d_brand[16];
long d_deleted;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,50 @@
/*
* mm_fee_bump_batch.pgc - mm 수수료율 일괄 인상 배치 (커서 + UPDATE + XA).
*
* 지정 card_type 의 수수료율 행을 커서로 훑어 rate_bps 를 delta 만큼 인상하고,
* 변경 이력을 mm_fee_rate_hist 에 남긴다. 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_fee_bump_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_fee_bump_batch [CARD_TYPE] [DELTA_BPS]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *card = (argc > 1) ? argv[1] : "CREDIT";
long delta = (argc > 2) ? atol(argv[2]) : 1;
long bumped = 0;
EXEC SQL INCLUDE mm_fee_bump_cpy;
strncpy(b_card, card, sizeof(b_card)-1); b_card[sizeof(b_card)-1] = 0;
b_delta = delta;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE mfb CURSOR FOR
SELECT merchant_id FROM mm_fee_rate WHERE card_type = :b_card ORDER BY merchant_id;
EXEC SQL OPEN mfb;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH mfb INTO :b_merch;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE mfb; tpabort(0); return 1; }
EXEC SQL UPDATE mm_fee_rate SET rate_bps = rate_bps + :b_delta, updated_at = now()
WHERE merchant_id = :b_merch AND card_type = :b_card;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE mfb; tpabort(0); return 1; }
bumped++;
}
EXEC SQL CLOSE mfb;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_fee_bump_batch COMMIT: card=%s delta=%ld 인상=%ld건\n", card, delta, bumped);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,6 @@
/* mm_fee_bump_cpy.h - mm_fee_bump_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char b_card[16];
long b_delta;
char b_merch[64];
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,39 @@
/*
* mm_hist_archive_batch.pgc - mm 현행 수수료율 이력 적재 배치 (INSERT..SELECT + XA).
*
* 현재 mm_fee_rate 스냅샷 전체를 mm_fee_rate_hist 에 한 문장으로 적재한다.
* hist_id 는 시퀀스 기준값 + row_number() 로 부여. ONE global XA transaction.
* host 변수는 전용 카피북 mm_hist_archive_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_hist_archive_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
(void) argc; (void) argv;
EXEC SQL INCLUDE mm_hist_archive_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT coalesce(max(hist_id),0) INTO :a_base FROM mm_fee_rate_hist;
if (sqlca.sqlcode < 0) { fprintf(stderr, "BASE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
EXEC SQL INSERT INTO mm_fee_rate_hist (hist_id, merchant_id, card_type, rate_bps, eff_date)
SELECT :a_base + row_number() OVER (ORDER BY merchant_id, card_type),
merchant_id, card_type, rate_bps, eff_date
FROM mm_fee_rate;
if (sqlca.sqlcode < 0) { fprintf(stderr, "INSERT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
a_rows = (long) sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_hist_archive_batch COMMIT: 이력적재=%ld건\n", a_rows);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* mm_hist_archive_cpy.h - mm_hist_archive_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long a_base;
long a_rows;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,40 @@
/*
* mm_limit_scale_batch.pgc - mm 등급별 한도 일괄 조정 배치 (UPDATE + IN 서브셀렉트 + XA).
*
* 지정 등급 가맹점(IN 서브셀렉트)의 mm_limit.daily_limit 을 pct 퍼센트로 스케일한다.
* 한 UPDATE 문으로 처리하고 변경행수를 sqlca.sqlerrd[2] 로 확인. ONE global XA.
* host 변수는 전용 카피북 mm_limit_scale_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_limit_scale_batch [GRADE] [PCT]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
const char *grade = (argc > 1) ? argv[1] : "S";
long pct = (argc > 2) ? atol(argv[2]) : 120;
EXEC SQL INCLUDE mm_limit_scale_cpy;
strncpy(s_grade, grade, sizeof(s_grade)-1); s_grade[sizeof(s_grade)-1] = 0;
s_pct = pct;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL UPDATE mm_limit
SET daily_limit = daily_limit * :s_pct / 100, updated_at = now()
WHERE merchant_id IN (SELECT merchant_id FROM mm_merchant_ext
WHERE grade = :s_grade AND status = 'ACTIVE');
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
s_scaled = (long) sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_limit_scale_batch COMMIT: grade=%s pct=%ld 조정=%ld건\n", grade, pct, s_scaled);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,6 @@
/* mm_limit_scale_cpy.h - mm_limit_scale_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char s_grade[8];
long s_pct;
long s_scaled;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,47 @@
/*
* mm_risk_rollup_batch.pgc - mm MCC 위험도 재산정 배치 (커서 + 가맹점수 기반 UPDATE + XA).
*
* mm_mcc 를 커서로 훑어 각 MCC 를 쓰는 가맹점 수를 세고, 그 규모에 따라 risk_level 을
* 재산정(UPDATE)한다. 규모가 클수록 높은 위험도. 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_risk_rollup_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_risk_rollup_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long rolled = 0;
(void) argc; (void) argv;
EXEC SQL INCLUDE mm_risk_rollup_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE mrr CURSOR FOR SELECT mcc FROM mm_mcc ORDER BY mcc;
EXEC SQL OPEN mrr;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH mrr INTO :k_mcc;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE mrr; tpabort(0); return 1; }
EXEC SQL SELECT count(*) INTO :k_merch FROM mm_merchant_ext WHERE mcc = :k_mcc;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE mrr; tpabort(0); return 1; }
k_level = (k_merch >= 100) ? 3 : (k_merch >= 10) ? 2 : 1;
EXEC SQL UPDATE mm_mcc SET risk_level = :k_level WHERE mcc = :k_mcc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE mrr; tpabort(0); return 1; }
rolled++;
}
EXEC SQL CLOSE mrr;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_risk_rollup_batch COMMIT: 재산정=%ld건\n", rolled);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,6 @@
/* mm_risk_rollup_cpy.h - mm_risk_rollup_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char k_mcc[8];
long k_merch;
long k_level;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,9 @@
/*
* mm_bin_distinct_dbio.h - mm DB copybook (mm_bin_distinct_dbio, part of libmmdbio.a).
* (count DISTINCT).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_BIN_DISTINCT_DBIO_H
#define MM_BIN_DISTINCT_DBIO_H
long mmdb_bin_distinct_issuer(const char *brand);
#endif /* MM_BIN_DISTINCT_DBIO_H */

View file

@ -0,0 +1,22 @@
/*
* mm_bin_distinct_dbio.pgc - mm 모듈 DB 접근 함수 (mm_bin_distinct_dbio), archived into libmmdbio.a.
* 카드브랜드별 고유 발급사 수 (count DISTINCT).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_bin_distinct_dbio.h"
/* card_brand 의 BIN 에서 고유 issuer_cd 수. */
long mmdb_bin_distinct_issuer(const char *brand)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_brand[16];
long h_v = 0;
EXEC SQL END DECLARE SECTION;
strncpy(h_brand, brand, sizeof(h_brand)-1); h_brand[sizeof(h_brand)-1] = 0;
EXEC SQL SELECT count(DISTINCT issuer_cd) INTO :h_v FROM mm_bin
WHERE card_brand = :h_brand AND status = 'ACTIVE';
if (sqlca.sqlcode < 0) return -1;
return h_v;
}

View file

@ -0,0 +1,9 @@
/*
* mm_fee_in_dbio.h - mm DB copybook (mm_fee_in_dbio, part of libmmdbio.a).
* (IN ).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_FEE_IN_DBIO_H
#define MM_FEE_IN_DBIO_H
long mmdb_fee_in_avg(const char *grade);
#endif /* MM_FEE_IN_DBIO_H */

View file

@ -0,0 +1,23 @@
/*
* mm_fee_in_dbio.pgc - mm 모듈 DB 접근 함수 (mm_fee_in_dbio), archived into libmmdbio.a.
* 특정 등급 가맹점의 평균 수수료율 (IN 서브셀렉트).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_fee_in_dbio.h"
/* grade 에 해당하는 가맹점(IN) 의 CREDIT 수수료율 평균(bps). */
long mmdb_fee_in_avg(const char *grade)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_grade[8];
long h_v = 0;
EXEC SQL END DECLARE SECTION;
strncpy(h_grade, grade, sizeof(h_grade)-1); h_grade[sizeof(h_grade)-1] = 0;
EXEC SQL SELECT coalesce(avg(rate_bps),0)::bigint INTO :h_v FROM mm_fee_rate
WHERE card_type = 'CREDIT'
AND merchant_id IN (SELECT merchant_id FROM mm_merchant_ext WHERE grade = :h_grade);
if (sqlca.sqlcode < 0) return -1;
return h_v;
}

View file

@ -0,0 +1,9 @@
/*
* mm_fee_rank_dbio.h - mm DB copybook (mm_fee_rank_dbio, part of libmmdbio.a).
* n번째 (window RANK).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_FEE_RANK_DBIO_H
#define MM_FEE_RANK_DBIO_H
int mmdb_fee_rank(const char *card_type, long rank, char *merch_out);
#endif /* MM_FEE_RANK_DBIO_H */

View file

@ -0,0 +1,26 @@
/*
* mm_fee_rank_dbio.pgc - mm 모듈 DB 접근 함수 (mm_fee_rank_dbio), archived into libmmdbio.a.
* 카드유형 내 수수료율 순위 n번째 가맹점 (window RANK).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_fee_rank_dbio.h"
/* card_type 파티션에서 rate_bps 내림차순 rank 번째 가맹점. */
int mmdb_fee_rank(const char *card_type, long rank, char *merch_out)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_ct[16], h_merch[64];
long h_rank = rank;
EXEC SQL END DECLARE SECTION;
strncpy(h_ct, card_type, sizeof(h_ct)-1); h_ct[sizeof(h_ct)-1] = 0;
EXEC SQL SELECT merchant_id INTO :h_merch FROM (
SELECT merchant_id, rank() OVER (PARTITION BY card_type ORDER BY rate_bps DESC) AS rk
FROM mm_fee_rate WHERE card_type = :h_ct) t
WHERE t.rk = :h_rank LIMIT 1;
if (sqlca.sqlcode == 100) { merch_out[0] = 0; return 0; }
if (sqlca.sqlcode < 0) return -1;
strcpy(merch_out, h_merch);
return 0;
}

View file

@ -0,0 +1,9 @@
/*
* mm_fx_upsert_dbio.h - mm DB copybook (mm_fx_upsert_dbio, part of libmmdbio.a).
* upsert (ON CONFLICT).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_FX_UPSERT_DBIO_H
#define MM_FX_UPSERT_DBIO_H
int mmdb_fx_upsert(const char *ccy, long rate_bps, const char *base_date);
#endif /* MM_FX_UPSERT_DBIO_H */

View file

@ -0,0 +1,25 @@
/*
* mm_fx_upsert_dbio.pgc - mm 모듈 DB 접근 함수 (mm_fx_upsert_dbio), archived into libmmdbio.a.
* 환율 마스터 upsert (ON CONFLICT).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_fx_upsert_dbio.h"
/* mm_fx_rate 에 통화별 환율을 upsert. */
int mmdb_fx_upsert(const char *ccy, long rate_bps, const char *base_date)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_ccy[8], h_date[16];
long h_rate = rate_bps;
EXEC SQL END DECLARE SECTION;
strncpy(h_ccy, ccy, sizeof(h_ccy)-1); h_ccy[sizeof(h_ccy)-1] = 0;
strncpy(h_date, base_date, sizeof(h_date)-1); h_date[sizeof(h_date)-1] = 0;
EXEC SQL INSERT INTO mm_fx_rate (ccy, rate_bps, base_date)
VALUES (:h_ccy, :h_rate, :h_date)
ON CONFLICT (ccy) DO UPDATE
SET rate_bps = EXCLUDED.rate_bps, base_date = EXCLUDED.base_date, updated_at = now();
if (sqlca.sqlcode < 0) { userlog("mmdb_fx_upsert FAIL [%d]", sqlca.sqlcode); return -1; }
return 0;
}

View file

@ -0,0 +1,9 @@
/*
* mm_grade_having_dbio.h - mm DB copybook (mm_grade_having_dbio, part of libmmdbio.a).
* (GROUP BY ... HAVING).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_GRADE_HAVING_DBIO_H
#define MM_GRADE_HAVING_DBIO_H
long mmdb_grade_having(long min_cnt);
#endif /* MM_GRADE_HAVING_DBIO_H */

View file

@ -0,0 +1,32 @@
/*
* mm_grade_having_dbio.pgc - mm 모듈 DB 접근 함수 (mm_grade_having_dbio), archived into libmmdbio.a.
* 가맹점 수 임계 초과 등급 수 (GROUP BY ... HAVING 커서).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_grade_having_dbio.h"
/* 등급별 활성 가맹점 수가 min_cnt 이상인 등급 개수. */
long mmdb_grade_having(long min_cnt)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_min = min_cnt, h_cnt;
char h_grade[8];
EXEC SQL END DECLARE SECTION;
long grades = 0;
EXEC SQL DECLARE mgh CURSOR FOR
SELECT grade, count(*) FROM mm_merchant_ext
WHERE status = 'ACTIVE' GROUP BY grade HAVING count(*) >= :h_min
ORDER BY grade;
EXEC SQL OPEN mgh;
if (sqlca.sqlcode < 0) return -1;
for (;;) {
EXEC SQL FETCH mgh INTO :h_grade, :h_cnt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE mgh; return -1; }
grades++;
}
EXEC SQL CLOSE mgh;
return grades;
}

View file

@ -0,0 +1,9 @@
/*
* mm_grade_insert_sel_dbio.h - mm DB copybook (mm_grade_insert_sel_dbio, part of libmmdbio.a).
* (INSERT..SELECT).
* Return convention: >=0 ok(), -1 = SQL error.
*/
#ifndef MM_GRADE_INSERT_SEL_DBIO_H
#define MM_GRADE_INSERT_SEL_DBIO_H
long mmdb_grade_insert_sel(const char *statdate);
#endif /* MM_GRADE_INSERT_SEL_DBIO_H */

View file

@ -0,0 +1,24 @@
/*
* mm_grade_insert_sel_dbio.pgc - mm 모듈 DB 접근 함수 (mm_grade_insert_sel_dbio), archived into libmmdbio.a.
* 등급별 가맹점 수 통계 일괄 적재 (INSERT..SELECT).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_grade_insert_sel_dbio.h"
/* 활성 가맹점을 등급별로 집계해 mm_grade_stat 에 한 문장으로 적재/갱신. */
long mmdb_grade_insert_sel(const char *statdate)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_date[16];
EXEC SQL END DECLARE SECTION;
strncpy(h_date, statdate, sizeof(h_date)-1); h_date[sizeof(h_date)-1] = 0;
EXEC SQL INSERT INTO mm_grade_stat (stat_date, grade, merch_cnt)
SELECT :h_date, grade, count(*) FROM mm_merchant_ext
WHERE status = 'ACTIVE' GROUP BY grade
ON CONFLICT (stat_date, grade) DO UPDATE
SET merch_cnt = EXCLUDED.merch_cnt, updated_at = now();
if (sqlca.sqlcode < 0) { userlog("mmdb_grade_insert_sel FAIL [%d]", sqlca.sqlcode); return -1; }
return (long) sqlca.sqlerrd[2];
}

View file

@ -0,0 +1,9 @@
/*
* mm_hist_purge_dbio.h - mm DB copybook (mm_hist_purge_dbio, part of libmmdbio.a).
* (DELETE range).
* Return convention: >=0 ok(), -1 = SQL error.
*/
#ifndef MM_HIST_PURGE_DBIO_H
#define MM_HIST_PURGE_DBIO_H
long mmdb_hist_purge(const char *cutoff);
#endif /* MM_HIST_PURGE_DBIO_H */

View file

@ -0,0 +1,20 @@
/*
* mm_hist_purge_dbio.pgc - mm 모듈 DB 접근 함수 (mm_hist_purge_dbio), archived into libmmdbio.a.
* 수수료율 이력 범위 삭제 후 삭제건수 반환 (DELETE range).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_hist_purge_dbio.h"
/* eff_date 이전 수수료율 이력 삭제, 삭제 행수 반환. */
long mmdb_hist_purge(const char *cutoff)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_cutoff[16];
EXEC SQL END DECLARE SECTION;
strncpy(h_cutoff, cutoff, sizeof(h_cutoff)-1); h_cutoff[sizeof(h_cutoff)-1] = 0;
EXEC SQL DELETE FROM mm_fee_rate_hist WHERE eff_date < :h_cutoff;
if (sqlca.sqlcode < 0) { userlog("mmdb_hist_purge FAIL [%d]", sqlca.sqlcode); return -1; }
return (long) sqlca.sqlerrd[2];
}

View file

@ -0,0 +1,9 @@
/*
* mm_issuer_corr_dbio.h - mm DB copybook (mm_issuer_corr_dbio, part of libmmdbio.a).
* BIN (correlated subquery + cursor).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_ISSUER_CORR_DBIO_H
#define MM_ISSUER_CORR_DBIO_H
long mmdb_issuer_corr_max_bin(void);
#endif /* MM_ISSUER_CORR_DBIO_H */

View file

@ -0,0 +1,33 @@
/*
* mm_issuer_corr_dbio.pgc - mm 모듈 DB 접근 함수 (mm_issuer_corr_dbio), archived into libmmdbio.a.
* 발급사별 BIN 수 상관 서브쿼리 순회 최대치 (correlated subquery + cursor).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_issuer_corr_dbio.h"
/* 각 발급사의 활성 BIN 수를 상관 서브쿼리로 뽑아 커서로 훑어 최대값 반환. */
long mmdb_issuer_corr_max_bin(void)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_issuer[16];
long h_bins;
EXEC SQL END DECLARE SECTION;
long peak = 0;
EXEC SQL DECLARE mic CURSOR FOR
SELECT i.issuer_cd,
(SELECT count(*) FROM mm_bin b WHERE b.issuer_cd = i.issuer_cd AND b.status = 'ACTIVE')
FROM mm_issuer i WHERE i.status = 'ACTIVE'
ORDER BY i.issuer_cd;
EXEC SQL OPEN mic;
if (sqlca.sqlcode < 0) return -1;
for (;;) {
EXEC SQL FETCH mic INTO :h_issuer, :h_bins;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE mic; return -1; }
if (h_bins > peak) peak = h_bins;
}
EXEC SQL CLOSE mic;
return peak;
}

View file

@ -0,0 +1,9 @@
/*
* mm_mcc_risk_cursor_dbio.h - mm DB copybook (mm_mcc_risk_cursor_dbio, part of libmmdbio.a).
* MCC (cursor + ).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_MCC_RISK_CURSOR_DBIO_H
#define MM_MCC_RISK_CURSOR_DBIO_H
long mmdb_mcc_weighted_risk(void);
#endif /* MM_MCC_RISK_CURSOR_DBIO_H */

View file

@ -0,0 +1,32 @@
/*
* mm_mcc_risk_cursor_dbio.pgc - mm 모듈 DB 접근 함수 (mm_mcc_risk_cursor_dbio), archived into libmmdbio.a.
* MCC 위험도 가맹점수 가중 합 (cursor + 누적).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_mcc_risk_cursor_dbio.h"
/* MCC 를 커서로 훑어 각 MCC 의 가맹점 수 x risk_level 을 누적. */
long mmdb_mcc_weighted_risk(void)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_mcc[8];
long h_risk, h_merch;
EXEC SQL END DECLARE SECTION;
long total = 0;
EXEC SQL DECLARE mrc CURSOR FOR
SELECT mcc, risk_level FROM mm_mcc ORDER BY mcc;
EXEC SQL OPEN mrc;
if (sqlca.sqlcode < 0) return -1;
for (;;) {
EXEC SQL FETCH mrc INTO :h_mcc, :h_risk;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE mrc; return -1; }
EXEC SQL SELECT count(*) INTO :h_merch FROM mm_merchant_ext WHERE mcc = :h_mcc;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE mrc; return -1; }
total += h_risk * h_merch;
}
EXEC SQL CLOSE mrc;
return total;
}

View file

@ -0,0 +1,9 @@
/*
* mm_noterm_dbio.h - mm DB copybook (mm_noterm_dbio, part of libmmdbio.a).
* (NOT EXISTS).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_NOTERM_DBIO_H
#define MM_NOTERM_DBIO_H
long mmdb_noterm_count(void);
#endif /* MM_NOTERM_DBIO_H */

View file

@ -0,0 +1,22 @@
/*
* mm_noterm_dbio.pgc - mm 모듈 DB 접근 함수 (mm_noterm_dbio), archived into libmmdbio.a.
* 단말 미보유 활성 가맹점 수 (NOT EXISTS).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_noterm_dbio.h"
/* mm_terminal 에 단말이 하나도 없는 활성 가맹점 수. */
long mmdb_noterm_count(void)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_v = 0;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(*) INTO :h_v FROM mm_merchant_ext m
WHERE m.status = 'ACTIVE'
AND NOT EXISTS (SELECT 1 FROM mm_terminal t
WHERE t.merchant_id = m.merchant_id AND t.status = 'ACTIVE');
if (sqlca.sqlcode < 0) return -1;
return h_v;
}

View file

@ -0,0 +1,9 @@
/*
* mm_rate_minmax_dbio.h - mm DB copybook (mm_rate_minmax_dbio, part of libmmdbio.a).
* / (min/max).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_RATE_MINMAX_DBIO_H
#define MM_RATE_MINMAX_DBIO_H
int mmdb_rate_minmax(const char *card_type, long *lo, long *hi);
#endif /* MM_RATE_MINMAX_DBIO_H */

View file

@ -0,0 +1,23 @@
/*
* mm_rate_minmax_dbio.pgc - mm 모듈 DB 접근 함수 (mm_rate_minmax_dbio), archived into libmmdbio.a.
* 카드유형 수수료율 최소/최대 (min/max).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_rate_minmax_dbio.h"
/* card_type 수수료율의 최소/최대 bps 를 반환. */
int mmdb_rate_minmax(const char *card_type, long *lo, long *hi)
{
EXEC SQL BEGIN DECLARE SECTION;
char h_ct[16];
long h_lo = 0, h_hi = 0;
EXEC SQL END DECLARE SECTION;
strncpy(h_ct, card_type, sizeof(h_ct)-1); h_ct[sizeof(h_ct)-1] = 0;
EXEC SQL SELECT coalesce(min(rate_bps),0), coalesce(max(rate_bps),0)
INTO :h_lo, :h_hi FROM mm_fee_rate WHERE card_type = :h_ct;
if (sqlca.sqlcode < 0) return -1;
*lo = h_lo; *hi = h_hi;
return 0;
}

View file

@ -0,0 +1,9 @@
/*
* mm_risk_case_dbio.h - mm DB copybook (mm_risk_case_dbio, part of libmmdbio.a).
* MCC (CASE ).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_RISK_CASE_DBIO_H
#define MM_RISK_CASE_DBIO_H
int mmdb_risk_case(long *lo, long *mid, long *hi);
#endif /* MM_RISK_CASE_DBIO_H */

View file

@ -0,0 +1,24 @@
/*
* mm_risk_case_dbio.pgc - mm 모듈 DB 접근 함수 (mm_risk_case_dbio), archived into libmmdbio.a.
* MCC 위험도 구간별 건수 (CASE 집계).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_risk_case_dbio.h"
/* risk_level 을 저/중/고 구간으로 CASE 분류해 각 건수 반환. */
int mmdb_risk_case(long *lo, long *mid, long *hi)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_lo = 0, h_mid = 0, h_hi = 0;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT
coalesce(sum(CASE WHEN risk_level <= 1 THEN 1 ELSE 0 END),0),
coalesce(sum(CASE WHEN risk_level = 2 THEN 1 ELSE 0 END),0),
coalesce(sum(CASE WHEN risk_level >= 3 THEN 1 ELSE 0 END),0)
INTO :h_lo, :h_mid, :h_hi FROM mm_mcc;
if (sqlca.sqlcode < 0) return -1;
*lo = h_lo; *mid = h_mid; *hi = h_hi;
return 0;
}

View file

@ -0,0 +1,9 @@
/*
* mm_status_filter_dbio.h - mm DB copybook (mm_status_filter_dbio, part of libmmdbio.a).
* (FILTER).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_STATUS_FILTER_DBIO_H
#define MM_STATUS_FILTER_DBIO_H
int mmdb_status_filter(long *active, long *hold, long *deleted);
#endif /* MM_STATUS_FILTER_DBIO_H */

View file

@ -0,0 +1,24 @@
/*
* mm_status_filter_dbio.pgc - mm 모듈 DB 접근 함수 (mm_status_filter_dbio), archived into libmmdbio.a.
* 가맹점 상태별 건수 한 행 분리집계 (FILTER).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_status_filter_dbio.h"
/* ACTIVE/HOLD/DELETED 건수를 FILTER 로 한 번에 반환. */
int mmdb_status_filter(long *active, long *hold, long *deleted)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_a = 0, h_h = 0, h_d = 0;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT
count(*) FILTER (WHERE status = 'ACTIVE'),
count(*) FILTER (WHERE status = 'HOLD'),
count(*) FILTER (WHERE status = 'DELETED')
INTO :h_a, :h_h, :h_d FROM mm_merchant_ext;
if (sqlca.sqlcode < 0) return -1;
*active = h_a; *hold = h_h; *deleted = h_d;
return 0;
}

View file

@ -0,0 +1,9 @@
/*
* mm_term_join_dbio.h - mm DB copybook (mm_term_join_dbio, part of libmmdbio.a).
* (JOIN).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_TERM_JOIN_DBIO_H
#define MM_TERM_JOIN_DBIO_H
long mmdb_active_terminals(void);
#endif /* MM_TERM_JOIN_DBIO_H */

View file

@ -0,0 +1,21 @@
/*
* mm_term_join_dbio.pgc - mm 모듈 DB 접근 함수 (mm_term_join_dbio), archived into libmmdbio.a.
* 활성 가맹점의 활성 단말 수 (JOIN).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_term_join_dbio.h"
/* mm_terminal 을 mm_merchant_ext 와 조인해 활성 가맹점의 활성 단말 수. */
long mmdb_active_terminals(void)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_v = 0;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(*) INTO :h_v
FROM mm_terminal t JOIN mm_merchant_ext m ON t.merchant_id = m.merchant_id
WHERE t.status = 'ACTIVE' AND m.status = 'ACTIVE';
if (sqlca.sqlcode < 0) { userlog("mmdb_active_terminals FAIL [%d]", sqlca.sqlcode); return -1; }
return h_v;
}

View file

@ -0,0 +1,9 @@
/*
* mm_wavg_limit_dbio.h - mm DB copybook (mm_wavg_limit_dbio, part of libmmdbio.a).
* (weighted average + JOIN).
* Return convention: >=0 ok, -1 = SQL error.
*/
#ifndef MM_WAVG_LIMIT_DBIO_H
#define MM_WAVG_LIMIT_DBIO_H
long mmdb_wavg_rate(void);
#endif /* MM_WAVG_LIMIT_DBIO_H */

View file

@ -0,0 +1,23 @@
/*
* mm_wavg_limit_dbio.pgc - mm 모듈 DB 접근 함수 (mm_wavg_limit_dbio), archived into libmmdbio.a.
* 한도 가중 수수료율 평균 (weighted average + JOIN).
* ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT.
*/
#include <string.h>
#include <userlog.h>
#include "mm_wavg_limit_dbio.h"
/* 일한도로 가중한 CREDIT 수수료율 평균 = sum(rate*limit)/sum(limit). */
long mmdb_wavg_rate(void)
{
EXEC SQL BEGIN DECLARE SECTION;
long h_num = 0, h_den = 0;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT coalesce(sum(f.rate_bps * l.daily_limit),0), coalesce(sum(l.daily_limit),0)
INTO :h_num, :h_den
FROM mm_fee_rate f JOIN mm_limit l ON f.merchant_id = l.merchant_id
WHERE f.card_type = 'CREDIT';
if (sqlca.sqlcode < 0) return -1;
if (h_den <= 0) return 0;
return h_num / h_den;
}