Function yaratish — RETURNS, LANGUAGE, SECURITY DEFINER

~20 daq · PostgreSQL · Funksiyalar va Protseduralar

Muammodan boshlaylik

Oldingi darsda DO bloki bilan PL/pgSQL ni sinab ko'rdik. Lekin DO bloki bir martalik — tugagandan keyin yo'qoladi. Agar bir xil kodni bir necha joyda ishlatish kerak bo'lsa — har safar qaytadan yozish kerak.

Masalan: foydalanuvchi balansini tekshirib, yetarli bo'lsa pul yechish. Bu logika bir necha yerda kerak — buyurtma berishda, pul o'tkazishda, obuna yangilashda. Har joyda bir xil kodni takrorlash — xatoga moyil, o'zgartirishda ko'p joy topib yangilash kerak.

Function — bu kodni bir joyda yozib, keyin nomini chaqirish. Bir marta yozing, istalgan joydan foydalaning.


Function nima?

Function — nom berilgan, parametr qabul qiladigan, natija qaytaradigan kod bloki. Yozgandan keyin jadval kabi bazada saqlanadi — o'chirilmaguncha ishlaydi.

Hayotiy o'xshatma: kafe menyusidagi taom. Oshpaz "kofeni qanday tayyorlash" retseptini bir marta yozadi. Har safar "bir kafe" deb buyurtma berilganda, retsept qaytadan o'qiladi va bajariladi. Function — shu retsept. Siz faqat nomini chaqirasiz — ichida nima bo'lishini bilmasan ham bo'ladi.

DO bloki:                    Function:
─────────────────────────    ──────────────────────────────
Bir martalik                 Saqlanib qoladi
Qayta ishlatib bo'lmaydi     Istalgan joydan chaqirish mumkin
Parametr yo'q                Parametr qabul qiladi

Eng oddiy function

CREATE OR REPLACE FUNCTION salomlash(ism TEXT)
RETURNS TEXT
LANGUAGE plpgsql
AS $
BEGIN
  RETURN 'Salom, ' || ism || '!';
END;
$;

Chaqirish:

SELECT salomlash('Ali');   -- 'Salom, Ali!'
SELECT salomlash('Vali');  -- 'Salom, Vali!'

CREATE OR REPLACE — allaqachon bor bo'lsa, yangilaydi. Yo'q bo'lsa, yaratadi.

RETURNS TEXT — funksiya nima qaytarishini bildiradi.

LANGUAGE plpgsql — qaysi tilda yozilgani.

AS $ ... $ — funksiya tanasi.


Sintaksis to'liq ko'rinishi

CREATE OR REPLACE FUNCTION funksiya_nomi(
  parametr1  tur1,
  parametr2  tur2 DEFAULT standart_qiymat
)
RETURNS qaytish_turi
LANGUAGE plpgsql
AS $
DECLARE
  -- o'zgaruvchilar
BEGIN
  -- kod
  RETURN natija;
END;
$;

Parametrlar

Oddiy parametrlar

CREATE OR REPLACE FUNCTION ikki_son_yig(a NUMERIC, b NUMERIC)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $
BEGIN
  RETURN a + b;
END;
$;

SELECT ikki_son_yig(15, 27);   -- 42
SELECT ikki_son_yig(100, 200); -- 300

DEFAULT qiymat

Ba'zi parametrlar ixtiyoriy bo'lishi mumkin — berilmasa standart qiymat ishlatiladi:

CREATE OR REPLACE FUNCTION chegirma_hisobla(
  narx      NUMERIC,
  foiz      NUMERIC DEFAULT 10   -- berilmasa 10% chegirma
)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $
BEGIN
  RETURN narx * (1 - foiz / 100.0);
END;
$;

SELECT chegirma_hisobla(1000000);          -- 900000  (10% chegirma)
SELECT chegirma_hisobla(1000000, 20);      -- 800000  (20% chegirma)
SELECT chegirma_hisobla(narx := 1000000, foiz := 15);  -- 850000 (nom bilan)

Qaytish turlari

Bitta qiymat

RETURNS INT
RETURNS TEXT
RETURNS NUMERIC
RETURNS BOOLEAN
RETURNS VOID       -- hech narsa qaytarmaydi

Jadval qatori — RETURNS TABLE

Funksiya bir nechta ustunli natija qaytarishi kerak bo'lsa:

CREATE OR REPLACE FUNCTION faol_foydalanuvchilar(min_buyurtma INT DEFAULT 1)
RETURNS TABLE (
  id    INT,
  ism   TEXT,
  soni  BIGINT
)
LANGUAGE plpgsql
AS $
BEGIN
  RETURN QUERY
    SELECT
      f.id,
      f.ism,
      COUNT(b.id)::BIGINT
    FROM foydalanuvchilar f
    JOIN buyurtmalar b ON f.id = b.foydalanuvchi_id
    GROUP BY f.id, f.ism
    HAVING COUNT(b.id) >= min_buyurtma
    ORDER BY COUNT(b.id) DESC;
END;
$;

Chaqirish — oddiy jadval kabi:

SELECT * FROM faol_foydalanuvchilar();
SELECT * FROM faol_foydalanuvchilar(min_buyurtma := 3);
SELECT ism FROM faol_foydalanuvchilar(2) WHERE soni > 5;

RETURN QUERY — so'rov natijasini funksiya natijasi sifatida qaytaradi.


LANGUAGE SQL — sodda funksiyalar uchun

Agar funksiya faqat SQL so'rovidan iborat bo'lsa — PL/pgSQL kerak emas, LANGUAGE sql yetarli. Tezroq va soddaroq:

CREATE OR REPLACE FUNCTION foydalanuvchi_buyurtmalari(foy_id INT)
RETURNS TABLE (mahsulot TEXT, summa NUMERIC)
LANGUAGE sql
AS $
  SELECT mahsulot, summa
  FROM buyurtmalar
  WHERE foydalanuvchi_id = foy_id
  ORDER BY summa DESC;
$;

PL/pgSQL kerak bo'lganda: shartlar, sikllar, o'zgaruvchilar, xatolarni ushlash kerak bo'lsa. Aks holda — LANGUAGE sql yetarli.


SECURITY DEFINER — kim nomidan ishlash

Bu parametr funksiyaning huquqlarini belgilaydi.

Standart — SECURITY INVOKER: Funksiya uni chaqirgan foydalanuvchi nomidan ishlaydi. Chaqiruvchi foydalanuvchilar jadvalini ko'ra olmasa — funksiya ham ko'ra olmaydi.

SECURITY DEFINER: Funksiya uni yaratgan foydalanuvchi nomidan ishlaydi. Chaqiruvchi jadvalga to'g'ridan-to'g'ri kirish huquqi bo'lmasa ham, funksiya orqali ishlay oladi.

SECURITY INVOKER:                    SECURITY DEFINER:
─────────────────────────────────    ─────────────────────────────────
Chaqiruvchi kim?    → uning huquqi   Yaratuvchi kim?     → uning huquqi
Ali chaqirsa        → Ali huquqi     Admin yaratgan       → Admin huquqi
Vali chaqirsa       → Vali huquqi    Ali yoki Vali chaqirsa → Admin huquqi

Hayotiy o'xshatma: kompaniya direktori imzolagan hujjat. Xodim direktorsiz hujjatni imzolay olmaydi. Lekin direktorning "muhrli" funksiyasi orqali hujjat rasmiy hisoblanadi. SECURITY DEFINER — o'sha muhur.

CREATE OR REPLACE FUNCTION umumiy_hisobot()
RETURNS TABLE (ism TEXT, jami NUMERIC)
LANGUAGE sql
SECURITY DEFINER       -- admin huquqi bilan ishlaydi
AS $
  SELECT f.ism, SUM(b.summa)
  FROM foydalanuvchilar f
  JOIN buyurtmalar b ON f.id = b.foydalanuvchi_id
  GROUP BY f.ism;
$;

-- Oddiy foydalanuvchi jadvalga kira olmaydi
-- Lekin funksiyani chaqira oladi — funksiya admin nomidan ishlaydi
GRANT EXECUTE ON FUNCTION umumiy_hisobot() TO oddiy_foydalanuvchi;

⚠️ Diqqat: SECURITY DEFINER — kuchli, lekin xavfli. Funksiya ichiga foydalanuvchi kiritgan matn to'g'ridan-to'g'ri SQL ga qo'shilmaydi — har doim parameterized query ishlatilsin.


Funksiyani ko'rish va o'chirish

-- Funksiya ta'rifini ko'rish
\df+ funksiya_nomi    -- psql da
SELECT prosrc FROM pg_proc WHERE proname = 'salomlash';

-- O'chirish
DROP FUNCTION salomlash(TEXT);
-- Parametr turi ko'rsatilishi shart — bir xil nomli bir nechta funksiya bo'lishi mumkin

-- Mavjud bo'lsa o'chirish
DROP FUNCTION IF EXISTS salomlash(TEXT);

Amaliy misol — to'liq funksiya

Foydalanuvchi ro'yxatdan o'tishi: email tekshirish, yaratish, profil yaratish — hammasi bitta funksiyada:

CREATE OR REPLACE FUNCTION foydalanuvchi_yaratish(
  p_ism    TEXT,
  p_email  TEXT,
  p_parol  TEXT
)
RETURNS INT     -- yangi foydalanuvchi ID si
LANGUAGE plpgsql
AS $
DECLARE
  yangi_id INT;
BEGIN
  -- Email ni normallashtirish
  p_email := LOWER(TRIM(p_email));

  -- Minimal validatsiya
  IF LENGTH(p_parol) < 8 THEN
    RAISE EXCEPTION 'Parol kamida 8 belgi bo''lishi kerak';
  END IF;

  -- Foydalanuvchi yaratish
  INSERT INTO foydalanuvchilar (ism, email, parol)
  VALUES (TRIM(p_ism), p_email, p_parol)
  RETURNING id INTO yangi_id;

  -- Profil yaratish
  INSERT INTO profillar (foydalanuvchi_id)
  VALUES (yangi_id);

  RETURN yangi_id;

EXCEPTION
  WHEN unique_violation THEN
    RAISE EXCEPTION 'Bu email allaqachon ro''yxatdan o''tgan: %', p_email;
END;
$;

Chaqirish:

SELECT foydalanuvchi_yaratish('Ali Karimov', 'ali@mail.com', 'parol123');
-- 1  (yangi ID)

SELECT foydalanuvchi_yaratish('Ali', 'ali@mail.com', 'boshqaparol');
-- ERROR: Bu email allaqachon ro'yxatdan o'tgan: ali@mail.com

Tez-tez uchraydigan xatolar

Bir xil nomli funksiyani o'chirishda parametr turini ko'rsatmaslik:

-- ❌ — qaysi salomlash? TEXT mi, INT mi?
DROP FUNCTION salomlash;
-- ERROR: function name "salomlash" is not unique

-- ✅ — parametr turi bilan aniq
DROP FUNCTION salomlash(TEXT);

PostgreSQL da bir xil nomli lekin har xil parametrli funksiyalar bo'lishi mumkin — bu overloading. Shu sababli o'chirishda parametr turi ko'rsatiladi.

RETURNS VOID da RETURN yozish:

-- ❌ — VOID funksiyada qiymat qaytarib bo'lmaydi
CREATE FUNCTION yangilash() RETURNS VOID AS $
BEGIN
  UPDATE ...;
  RETURN 1;   -- xato
END;
$ LANGUAGE plpgsql;

-- ✅ — RETURN; yoki umuman yozmaslik
BEGIN
  UPDATE ...;
  RETURN;   -- bo'sh, yoki umuman yozilmaydi
END;

Xulosa

Function — saqlanadigan, qayta ishlatiladigan kod bloki.

CREATE OR REPLACE FUNCTION nom(parametrlar)
RETURNS tur
LANGUAGE plpgsql / sql
AS $ ... $;

Qaytish turlari:

  • RETURNS tur — bitta qiymat
  • RETURNS TABLE(...) + RETURN QUERY — ko'p qatorli natija
  • RETURNS VOID — hech narsa qaytarmaydi

Huquqlar:

  • SECURITY INVOKER (standart) — chaqiruvchi huquqi
  • SECURITY DEFINER — yaratuvchi huquqi, ehtiyot bo'lish kerak

LANGUAGE sql — faqat SQL kerak bo'lsa, sodda va tezroq. LANGUAGE plpgsql — shartlar, sikllar, xatolar kerak bo'lsa.


Keyingi dars

33-dars: Stored Procedure — Function dan farqi, CALL