03000 sql statement not yet complete λ?
PostgreSQL μλ¬ μ½λ 03000μ "sql statement not yet complete" λ‘, SQL κ΅¬λ¬Έμ΄ μμ ν μλ£λμ§ μμ μνμμ λ€μ λͺ
λ Ήμ΄ μ€νλκ±°λ νΉμ ν¨μκ° νΈμΆλ λ λ°μνλ κ²½κ³ μ± μλ¬μ
λλ€. μ£Όλ‘ PL/pgSQL ν¨μ, νλ‘μμ , λλ λμ SQL μ€ν λΈλ‘ λ΄μμ 컀μ(cursor)λ 쿼리 κ²°κ³Ό μ²λ¦¬ λμ€ λΆμμ ν μνλ‘ λ€λ₯Έ μμ
μ μλν λ λνλ©λλ€. μ΄ μλ¬λ λ¨μ SELECT μ€λ₯κ° μλλΌ μ€ν νλ¦ μ μ΄(flow control) μ λ°μ νκ² μ°κ΄λμ΄ μμ΄, μ€λ¬΄μμ λμΉκΈ° μ¬μ΄ μ νμ μν©λλ€.
μ£Όμ λ°μ μμΈ
1. 컀μ(Cursor)κ° μ΄λ¦° μνμμ λΆμμ ν νμΉ(FETCH) μ²λ¦¬
컀μλ₯Ό OPENν ν FETCHλ‘ λ°μ΄ν°λ₯Ό λͺ¨λ μλΉνμ§ μμ μ± λ€λ₯Έ SQL μμ
μ μννκ±°λ 컀μλ₯Ό λ«μ§ μμΌλ©΄ μ΄ μλ¬κ° λ°μν μ μμ΅λλ€. νΉν λ°λ³΅λ¬Έ(loop) λ΄μμ μ‘°κΈ° νμΆ(EXIT)νκ±°λ μμΈ μ²λ¦¬ μμ΄ μ»€μλ₯Ό λ°©μΉνλ ν¨ν΄μ΄ μ£Όμ μμΈμ
λλ€.
2. PL/pgSQL λμ SQL(EXECUTE) λΈλ‘μ λΆμμ ν λ¬Έμμ΄ μ‘°ν©
EXECUTE λͺ
λ Ήμ μ λ¬λλ SQL λ¬Έμμ΄μ΄ λ°νμμ λμ μΌλ‘ μμ±λ λ, 쑰건 λΆκΈ° μ€λ₯λ λ³μ λλ½μΌλ‘ μΈν΄ SQL κ΅¬λ¬Έμ΄ μ€κ°μ μ리거λ μΈλ―Έμ½λ‘ (;)μ΄ λλ½λ μ± μ€ν μλκ° λ°μν μ μμ΅λλ€. PostgreSQL νμκ° ν΄λΉ λ¬Έμμ΄μ μμ ν SQLλ‘ μΈμνμ§ λͺ»νλ©΄ 03000 μλ¬λ₯Ό λ°νν©λλ€.
3. νΈλμμ λΈλ‘ λ΄λΆμμμ λΆμμ ν λ³΅ν© λͺ λ Ή μ²λ¦¬
BEGIN ... END λΈλ‘ μμμ μ¬λ¬ DML λλ DDL μμ
μ μ²λ¦¬ν λ, μμ λͺ
λ Ήμ΄ λ΄λΆμ μΌλ‘ μμ§ μλ£λμ§ μμ μμ μ νμ λͺ
λ Ήμ΄ ν¬μ
λλ©΄ μ΄ μλ¬κ° νΈλ¦¬κ±°λ©λλ€. νΉν PostgreSQLμ νμ΄νλΌμΈ λͺ¨λ(pipelining mode)λ μΌλΆ ν΄λΌμ΄μΈνΈ λΌμ΄λΈλ¬λ¦¬μμ λΉλκΈ° μ€νμ μ¬μ©ν λ λ°μ λΉλκ° λμ΅λλ€.
ν΄κ²° λ°©λ²
μμΈ 1 ν΄κ²° - 컀μ μ μ μ’ λ£ λ³΄μ₯
컀μλ₯Ό μ¬μ©ν λλ λ°λμ CLOSE ꡬ문μΌλ‘ λͺ
μμ μΌλ‘ λ«μμΌ νλ©°, μμΈ μν©μμλ 컀μκ° λ«νλλ‘ EXCEPTION λΈλ‘μ νμ©ν΄μΌ ν©λλ€.
μλͺ»λ μμ (μλ¬ λ°μ κ°λ₯μ± μμ):
DO $$
DECLARE
cur CURSOR FOR SELECT id, name FROM users WHERE active = true;
rec RECORD;
BEGIN
OPEN cur;
FETCH cur INTO rec;
-- μμ
μ€ μ‘°κΈ° νμΆ λλ 컀μλ₯Ό λ«μ§ μμ
IF rec.id = 1 THEN
RETURN; -- 컀μκ° μ΄λ¦° μ±λ‘ μ’
λ£λ¨
END IF;
-- CLOSE cur; λλ½
END;
$$;
μ¬λ°λ₯Έ μμ (컀μ μμ μ²λ¦¬):
DO $$
DECLARE
cur CURSOR FOR SELECT id, name FROM users WHERE active = true;
rec RECORD;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO rec;
EXIT WHEN NOT FOUND;
-- μ€μ μμ
μν
RAISE NOTICE 'Processing user: %', rec.name;
-- μ‘°κΈ° μ’
λ£κ° νμν κ²½μ°μλ CLOSE ν νμΆ
IF rec.id = 999 THEN
CLOSE cur;
RETURN;
END IF;
END LOOP;
CLOSE cur; -- λ°λμ λͺ
μμ μ’
λ£
EXCEPTION
WHEN OTHERS THEN
-- μμΈ λ°μ μμλ 컀μ λ«κΈ°
IF cur IS NOT NULL THEN
CLOSE cur;
END IF;
RAISE;
END;
$$;
μμΈ 2 ν΄κ²° - λμ SQL λ¬Έμμ΄ κ²μ¦
λμ μΌλ‘ ꡬμ±νλ SQL λ¬Έμμ΄μ μ€ν μ μ λ°λμ λ‘κ·Έλ‘ μΆλ ₯νκ±°λ format() ν¨μλ₯Ό νμ©ν΄ μμ νκ² μ‘°ν©ν΄μΌ ν©λλ€.
μλͺ»λ μμ (λ¬Έμμ΄μ΄ λΆμμ ν μ μμ):
CREATE OR REPLACE FUNCTION get_table_count(p_table_name TEXT)
RETURNS INTEGER AS $$
DECLARE
v_sql TEXT;
v_count INTEGER;
BEGIN
-- μν: μ
λ ₯κ° κ²μ¦ μμ΄ μ§μ λ¬Έμμ΄ μ°κ²°
v_sql := 'SELECT COUNT(*) FROM ' || p_table_name;
-- λ§μ½ p_table_nameμ΄ NULLμ΄κ±°λ λΉμ΄μμΌλ©΄ λΆμμ ν SQL μμ±
EXECUTE v_sql INTO v_count;
RETURN v_count;
END;
$$ LANGUAGE plpgsql;
μ¬λ°λ₯Έ μμ (μμ ν λμ SQL ꡬμ±):
CREATE OR REPLACE FUNCTION get_table_count(p_schema TEXT, p_table_name TEXT)
RETURNS INTEGER AS $$
DECLARE
v_sql TEXT;
v_count INTEGER;
BEGIN
-- μ
λ ₯κ° NULL κ²μ¦
IF p_table_name IS NULL OR trim(p_table_name) = '' THEN
RAISE EXCEPTION 'Table name cannot be NULL or empty';
END IF;
-- format() ν¨μλ‘ μμ νκ² μλ³μ μ²λ¦¬ (%I = identifier quoting)
v_sql := format(
'SELECT COUNT(*) FROM %I.%I',
COALESCE(p_schema, 'public'),
p_table_name
);
-- λλ²κΉ
μ© λ‘κ·Έ (μ΄μ νκ²½μμλ DEBUG λ λ²¨λ‘ μ‘°μ )
RAISE DEBUG 'Executing SQL: %', v_sql;
EXECUTE v_sql INTO v_count;
RETURN v_count;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to execute dynamic SQL [%]: %', v_sql, SQLERRM;
END;
$$ LANGUAGE plpgsql;
-- νΈμΆ μμ
SELECT get_table_count('public', 'users');
SELECT get_table_count(NULL, 'orders');
μμΈ 3 ν΄κ²° - νΈλμμ λΈλ‘ λ΄ λͺ λ Ή μμ 보μ₯
λ³΅ν© νΈλμμ
λ΄μμ λͺ
λ Ήμ μλ£λ₯Ό 보μ₯νκ³ , λΉλκΈ° μ²λ¦¬λ νμ΄νλΌμΈμ μ¬μ©νλ κ²½μ° λͺ
μμ μΈ λκΈ°ν ν¬μΈνΈλ₯Ό λμ΄μΌ ν©λλ€.
-- μ¬λ°λ₯Έ νΈλμμ
λΈλ‘ μ²λ¦¬ μμ
DO $$
BEGIN
-- 1λ¨κ³: μμ ν
μ΄λΈ μμ± (μμ ν μλ£ ν λ€μ λ¨κ³λ‘)
CREATE TEMP TABLE IF NOT EXISTS temp_processing (
id SERIAL PRIMARY KEY,
data TEXT,
processed_at TIMESTAMPTZ DEFAULT NOW()
);
-- 2λ¨κ³: λ°μ΄ν° μ½μ
INSERT INTO temp_processing (data)
SELECT name FROM users WHERE created_at > NOW() - INTERVAL '1 day';
-- 3λ¨κ³: κ²°κ³Ό νμΈ ν μ²λ¦¬ (κ° λ¨κ³κ° μλ£λ ν μ€ν)
UPDATE temp_processing
SET data = upper(data)
WHERE data IS NOT NULL;
RAISE NOTICE 'Processing complete. Rows affected: %', (SELECT COUNT(*) FROM temp_processing);
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'Transaction failed: %', SQLERRM;
END;
$$;
μλ°© λ°©λ²
1. 컀μ λ° λμ SQLμ λν Wrapper ν¨μ ν¨ν΄ λμ
λ°λ³΅μ μΌλ‘ 컀μλ λμ SQLμ μ¬μ©νλ λ‘μ§μ λ³λμ ν¬νΌ ν¨μλ‘ μΆμννμ¬ μ΄κΈ°-μ¬μ©-λ«κΈ° ν¨ν΄μ μΌκ΄λκ² μ μ©νμΈμ. λͺ¨λ 컀μ μ¬μ© ν¨μμ EXCEPTION WHEN OTHERS λΈλ‘μ λ°λμ ν¬ν¨μν€κ³ , ν΄λΉ λΈλ‘ λ΄μμ 컀μ μνλ₯Ό μ΄κΈ°ννλ μ½λλ₯Ό νμ€ ν
νλ¦ΏμΌλ‘ ν λ΄μ 곡μ νλ κ²μ΄ μ’μ΅λλ€. μ½λ 리뷰 체ν¬λ¦¬μ€νΈμ "컀μ CLOSE μ¬λΆ", "λμ SQL NULL κ²μ¦ μ¬λΆ"λ₯Ό μΆκ°νλ©΄ μ¬μ μλ°©μ ν° ν¨κ³Όκ° μμ΅λλ€.
-- ν κ³΅μ© μ»€μ μ²λ¦¬ νμ€ ν
νλ¦Ώ
CREATE OR REPLACE FUNCTION safe_cursor_template()
RETURNS VOID AS $$
DECLARE
cur REFCURSOR;
rec RECORD;
BEGIN
OPEN cur FOR SELECT * FROM target_table;
LOOP
FETCH cur INTO rec;
EXIT WHEN NOT FOUND;
-- λΉμ¦λμ€ λ‘μ§
END LOOP;
CLOSE cur;
EXCEPTION
WHEN OTHERS THEN
IF cur IS NOT NULL THEN
CLOSE cur;
END IF;
RAISE;
END;
$$ LANGUAGE plpgsql;
2. PostgreSQL λ‘κ·Έ λ 벨 λ° log_min_messages μ€μ μ΅μ ν
κ°λ° λ° μ€ν
μ΄μ§ νκ²½μμλ postgresql.confμ log_min_messagesλ₯Ό DEBUG1 μ΄μμΌλ‘ μ€μ νμ¬ λΆμμ ν SQL μ€ν μλλ₯Ό μ‘°κΈ°μ ν¬μ°©νμΈμ. λν pg_stat_activity λ·°λ₯Ό μ κΈ°μ μΌλ‘ λͺ¨λν°λ§νμ¬ state 컬λΌμ΄ idle in transactionμΌλ‘ μ€λ 머무λ μΈμ
μ νμ§νλ©΄, 컀μλ νΈλμμ
μ΄ λ―Έμλ£ μνλ‘ λ°©μΉλλ μν©μ μ¬μ μ μ°¨λ¨ν μ μμ΅λλ€.
-- λ―Έμλ£ νΈλμμ
μΈμ
λͺ¨λν°λ§ 쿼리
SELECT
pid,
usename,
application_name,
state,
query,
now() - xact_start AS transaction_duration
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'active')
AND xact_start IS NOT NULL
AND now() - xact_start > INTERVAL '5 minutes'
ORDER BY transaction_duration DESC;
κ΄λ ¨ μλ¬
-
25P01(no active SQL transaction): νΈλμμ λΈλ‘ μΈλΆμμ νΈλμμ μ μ΄ λͺ λ Ήμ μ€νν λ λ°μνλ©°,03000κ³Ό ν¨κ» νΈλμμ νλ¦ μ€λ₯μ λν μ¬λ‘μ λλ€. -
34000(invalid cursor name): 컀μ μ΄λ¦μ΄ μλͺ» μ°Έμ‘°λκ±°λ μ΄λ―Έ λ«ν 컀μλ₯Ό λ€μ μ¬μ©ν λ λ°μνλ©°, 컀μ κ΄λ ¨03000μλ¬μ νμ μ¦μμΌλ‘ λνλλ κ²½μ°κ° λ§μ΅λλ€. -
42601(syntax error): λμ SQL λ¬Έμμ΄ μ‘°ν© μ€λ₯ μ03000κ³Ό μ μ¬ν λ§₯λ½μμ λ°μνλ©°, λ μλ¬κ° λμμ λ‘κ·Έμ κΈ°λ‘λ κ²½μ° λμ SQL κ΅¬μ± λ‘μ§μ μ°μ μ κ²ν΄μΌ ν©λλ€. -
40001(serialization failure): νΈλμμ 격리 μμ€ κ΄λ ¨ μΆ©λλ‘ λ°μνλ©°, 볡μ‘ν νΈλμμ λΈλ‘μμ03000κ³Ό ν¨κ» λνλ κ²½μ° μ 체 νΈλμμ μ€κ³λ₯Ό μ¬κ²ν ν νμκ° μμ΅λλ€.









