일별 줄은 멀쩡한데(12500, 3400, 900원), TOTAL은 0125003400900 KRW로 찍혀요. 왜 0이 하나 더 붙죠?
const grand = rows.reduce((acc, r) => acc + r.cents, 0);
console.log(`TOTAL ${won(grand)}`);
tsc는 통과하고, Postgres 16은 docker에서 돌려요. reduce는 늘 쓰던 그대로인데요...
일별 줄은 멀쩡한데(12500, 3400, 900원), TOTAL은 0125003400900 KRW로 찍혀요. 왜 0이 하나 더 붙죠?
const grand = rows.reduce((acc, r) => acc + r.cents, 0);
console.log(`TOTAL ${won(grand)}`);
tsc는 통과하고, Postgres 16은 docker에서 돌려요. reduce는 늘 쓰던 그대로인데요...
Daily lines are fine (12500, 3400, 900 won), but TOTAL prints 0125003400900 KRW. Why extra zero?
const grand = rows.reduce((acc, r) => acc + r.cents, 0);
console.log(`TOTAL ${won(grand)}`);
tsc passes, Postgres 16 in docker. Same reduce as always...
cents가 bigint나 numeric 컬럼인가요? 드라이버가 그런 값은 문자열로 돌려주는데, 0 + "12500"은 "012500"이에요. tsc는 그걸 못 봐요.
Is cents a bigint or numeric column? The driver hands those back as strings, and 0 + "12500" is "012500". tsc can't see that.
네, 거의 확실히 그거예요. 0125003400900은 그냥 문자열이 이어 붙은 거고, pg는 bigint와 numeric을 문자열로 돌려줘요. reduce에서 Number(r.cents)를 쓰거나, 값이 들어간다면 쿼리에서 cents::int로 캐스팅하면 돼요. 값이 커질 수 있으면 BigInt를 쓰고 마지막에 포맷하세요.
yeah that's almost certainly it, 0125003400900 is just the strings concatenated, pg returns bigint and numeric as strings. either Number(r.cents) in the reduce, or cast in the query with cents::int if the values fit. if they might get big, use BigInt and format at the end.
잘못된 건 없어요, 합계가 그냥 아주 정직할 뿐이에요. Postgres는 텍스트를 보내고, JavaScript는 그걸 이어 붙여요. 문자열 이어 붙이기로 하는 회계인데 tsc는 "괜찮아 보이는데요"래요. 그럴 만하죠.
Nothing is wrong, your total is just very honest. Postgres sends text, JavaScript glues it. Accounting by string concatenation, and tsc says "looks fine". Of course it does.
맞는 말인데, 진짜 해결책은 row를 cents: string으로 타이핑해서 tsc가 +에서 에러를 내게 하는 거예요. 여기서 거짓말하는 건 드라이버 타입이에요.
fair, though the real fix is typing the row as cents: string so tsc complains at the +, the driver types are what lie here.
그래요, 드라이버 탓을 하세요. 타입도 거짓말하니까요.
그러게요, 타입은 거짓말하지만 적어도 문자열은 마음으로는 합쳐지잖아요
cents::int에 주의할 점이 하나 있어요. 대신 SQL에서 합계를 내면 결과가 다시 문자열이에요. sum(int)는 bigint를, sum(bigint)는 numeric을 반환하고, 드라이버는 둘 다 텍스트로 줘요. 그러니 어차피 Number()는 필요하고, 행마다 하는 대신 마지막에 한 번만 하면 돼요.
그래도 쿼리에서 합계를 내는 게 더 나은 방법이에요. 데이터베이스가 한 번에 처리해 주니까요. 집계 함수의 반환 타입은 Postgres 집계 함수 문서에 나와 있어요.
One caveat on cents::int: if you sum in SQL instead, the result is a string again. sum(int) returns bigint and sum(bigint) returns numeric, and the driver gives you both as text. So the Number() is needed anyway, just once at the end instead of per row.
Summing in the query is still the better place, the database does it in one pass. The aggregate return types are in the Postgres aggregate docs.
아니면 pg-protocol의 setTypeParser(20, Number)로 파서를 한 번만 등록하면 int8이 전부 number로 와요. 값이 2^53을 넘기기 전까지는 문제없어요.
Or register a parser once with setTypeParser(20, Number) from pg-protocol, and every int8 comes back as a number. Just fine until values pass 2^53.
웃긴 건 won()이 문자열 "12500"도 숫자랑 똑같이 포맷해서 일별 줄은 괜찮아 보였다는 거예요. +에서만 드러났어요. 그리고 앞의 0은 제 초기값 0이 텍스트가 된 거였고요. MDN의 덧셈 연산자 페이지에 분명히 나와 있어요, 한쪽이라도 문자열이면 이어 붙인다고요. 알고는 있었는데, reduce 안에서는 처음이라...
Funny part is the daily lines looked fine because won() formats the string "12500" the same as a number. Only the + showed it. And the extra zero in front was just my initial 0 becoming text. MDN's addition operator page says it plainly, if either side is a string, concatenate. I knew this, but never in a reduce...
네, 제 경우에 맞아요, 모든 reduce마다가 아니라 한 곳에서 해결되니까요. 그런데 setTypeParser는 pg-protocol이 아니라 pg-types에서 오는 걸로 알아요...
Yes, this fits my case, one place instead of every reduce. I think setTypeParser comes from pg-types though, not pg-protocol...
database/sql로 만든 작은 도구가 목록을 반만 돌려줬어. 에러 없이. 코드는 이래. 컨텍스트에 데드라인이 있었어. 가져오는 도중에 데드라인이 지나서 rows.Next()가 false를 줬고, 루프가 끝났고, 함수는…
수업 프로젝트로 Go로 작은 job runner를 만들고 있어요. 환경은 database/sql을 통해 쓰는 pgx v5, 로컬 Postgres 16이고, jobs 테이블에 행이 600만 개쯤 있어요. 거의 다 done이고…
작업 큐를 그냥 테이블에 둠. 워커 하나는 Python, 하나는 Go. BEGIN, 그다음 SELECT id, payload FROM jobs WHERE status = 'queued' ORDER BY id LIMIT 1 F…