PostgreSQL เชฎเชพเช‚ เชฐเซ‹ เชฒเซ‡เชตเชฒ เชธเชฟเช•เซเชฏเซเชฐเชฟเชŸเซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชฐเซ‹เชฒ-เช†เชงเชพเชฐเชฟเชค เชเช•เซเชธเซ‡เชธ เชฎเซ‹เชกเชฒเชจเซ‹ เช…เชฎเชฒ เช•เชฐเชตเซ‹

เชฅเซ€เชฎ เชตเชฟเช•เชพเชธ PostgreSQL เชฎเชพเช‚ เชชเช‚เช•เซเชคเชฟ เชธเซเชคเชฐเชจเซ€ เชธเซเชฐเช•เซเชทเชพเชจเชพ เช…เชฎเชฒเซ€เช•เชฐเชฃ เชชเชฐเชจเซ‹ เช…เชญเซเชฏเชพเชธ ะธ เชตเชฟเช—เชคเชตเชพเชฐ เชœเชตเชพเชฌ เชฎเชพเชŸเซ‡ เชชเชฐ เชเช• เชŸเชฟเชชเซเชชเชฃเซ€.

เช‰เชชเชฏเซ‹เช—เชฎเชพเช‚ เชฒเซ‡เชตเชพเชคเซ€ เชตเซเชฏเซ‚เชนเชฐเชšเชจเชพ "เชกเซ‡เชŸเชพเชฌเซ‡เชเชฎเชพเช‚ เชฌเชฟเชเชจเซ‡เชธ เชฒเซ‹เชœเชฟเช•" เช–เซเชฏเชพเชฒเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ‡ เช›เซ‡, เชœเซ‡เชจเซเช‚ เช…เชนเซ€เช‚ เชฅเซ‹เชกเซ€ เชตเชงเซ เชตเชฟเช—เชคเชฎเชพเช‚ เชตเชฐเซเชฃเชจ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เช›เซ‡ - PostgreSQL เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชพเชฐเซเชฏเซ‹เชจเชพ เชธเซเชคเชฐเซ‡ เชฌเชฟเชเชจเซ‡เชธ เชฒเซ‹เชœเซ€เช•เชจเชพ เช…เชฎเชฒเซ€เช•เชฐเชฃ เชชเชฐเชจเซ‹ เช…เชญเซเชฏเชพเชธ

เชธเซˆเชฆเซเชงเชพเช‚เชคเชฟเช• เชญเชพเช— เชฆเชธเซเชคเชพเชตเซ‡เชœเซ€เช•เชฐเชฃเชฎเชพเช‚ เชธเชพเชฐเซ€ เชฐเซ€เชคเซ‡ เชตเชฐเซเชฃเชตเซ‡เชฒ เช›เซ‡ PostgreSQL - เชชเช‚เช•เซเชคเชฟ เชธเช‚เชฐเช•เซเชทเชฃ เชจเซ€เชคเชฟเช“. เชจเซ€เชšเซ‡ เชเช• เชตเซเชฏเชตเชนเชพเชฐเซ เช…เชฎเชฒเซ€เช•เชฐเชฃ เช›เซ‡ เชšเซ‹เช•เซเช•เชธ เชตเซเชฏเชตเชธเชพเชฏ เช•เชพเชฐเซเชฏ - เชกเซ‡เชŸเชพ เชเช•เซเชธเซ‡เชธ เชฎเชพเชŸเซ‡เชจเซ‹ เชฐเซ‹เชฒ เชฎเซ‹เชกเซ‡เชฒ.

PostgreSQL เชฎเชพเช‚ เชฐเซ‹ เชฒเซ‡เชตเชฒ เชธเชฟเช•เซเชฏเซเชฐเชฟเชŸเซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชฐเซ‹เชฒ-เช†เชงเชพเชฐเชฟเชค เชเช•เซเชธเซ‡เชธ เชฎเซ‹เชกเชฒเชจเซ‹ เช…เชฎเชฒ เช•เชฐเชตเซ‹

เชฒเซ‡เช–เชฎเชพเช‚ เช•เช‚เชˆ เชจเชตเซเช‚ เชจเชฅเซ€, เช•เซ‹เชˆ เช›เซเชชเชพเชฏเซ‡เชฒ เช…เชฐเซเชฅ เช•เซ‡ เช—เซเชชเซเชค เชœเซเชžเชพเชจ เชจเชฅเซ€. เชธเซˆเชฆเซเชงเชพเช‚เชคเชฟเช• เชตเชฟเชšเชพเชฐเชจเชพ เชตเซเชฏเชตเชนเชพเชฐเชฟเช• เช…เชฎเชฒเซ€เช•เชฐเชฃ เชตเชฟเชถเซ‡ เชฎเชพเชคเซเชฐ เชเช• เชธเซเช•เซ‡เชš. เชœเซ‹ เช•เซ‹เชˆเชจเซ‡ เชฐเชธ เชนเซ‹เชฏ เชคเซ‹ เชตเชพเช‚เชšเซ‹. เชœเซ‹ เชคเชฎเชจเซ‡ เชฐเชธ เชจเชฅเซ€, เชคเซ‹ เชคเชฎเชพเชฐเซ‹ เชธเชฎเชฏ เชฌเช—เชพเชกเซ‹ เชจเชนเซ€เช‚.

เชธเชฎเชธเซเชฏเชพเชจเซ€ เชฐเชšเชจเชพ

เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพเชจเซ€ เชญเซ‚เชฎเชฟเช•เชพ เช…เชจเซเชธเชพเชฐ เชฆเชธเซเชคเชพเชตเซ‡เชœ เชœเซ‹เชตเชพ/เชฆเชพเช–เชฒ เช•เชฐเชตเชพ/เชธเช‚เชถเซ‹เชงเชฟเชค เช•เชฐเชตเชพ/เช•เชพเชขเซ€ เชจเชพเช–เชตเชพเชจเซ€ เชเช•เซเชธเซ‡เชธเชจเซ‡ เชชเซเชฐเชคเชฟเชฌเช‚เชงเชฟเชค เช•เชฐเชตเซ€ เชœเชฐเซ‚เชฐเซ€ เช›เซ‡. เชญเซ‚เชฎเชฟเช•เชพ เช•เซ‹เชทเซเชŸเช•เชฎเชพเช‚เชจเซ€ เชเชจเซเชŸเซเชฐเซ€เชจเซ‹ เชธเช‚เชฆเชฐเซเชญ เช†เชชเซ‡ เช›เซ‡ เชญเซ‚เชฎเชฟเช•เชพ เชŸเซ‡เชฌเชฒ เชธเชพเชฅเซ‡ เช…เชจเซ‡เช•-เชฅเซ€-เช˜เชฃเชพ เชธเช‚เชฌเช‚เชง เชธเชพเชฅเซ‡ เชธเช‚เช•เชณเชพเชฏเซ‡เชฒ เชตเชชเชฐเชพเชถเช•เชฐเซเชคเชพเช“. เช•เซ‹เชทเซเชŸเช•เซ‹เชจเชพ เช…เชฎเชฒเซ€เช•เชฐเชฃเชจเซ€ เชตเชฟเช—เชคเซ‹, เชคเซเชšเซเช›เชคเชพเชจเซ‡ เช•เชพเชฐเชฃเซ‡, เช…เชตเช—เชฃเชตเชพเชฎเชพเช‚ เช†เชตเซ€ เช›เซ‡. เชตเชฟเชทเชฏ เชตเชฟเชธเซเชคเชพเชฐ เชธเชพเชฅเซ‡ เชธเช‚เชฌเช‚เชงเชฟเชค เชšเซ‹เช•เซเช•เชธ เช…เชฎเชฒเซ€เช•เชฐเชฃ เชตเชฟเช—เชคเซ‹ เชชเชฃ เช…เชตเช—เชฃเชตเชพเชฎเชพเช‚ เช†เชตเซ€ เช›เซ‡.

ะ ะตะฐะปะธะทะฐั†ะธั

เชญเซ‚เชฎเชฟเช•เชพเช“, เชฏเซ‹เชœเชจเชพเช“, เช•เซ‹เชทเซเชŸเช•เซ‹ เชฌเชจเชพเชตเซ‹

เชกเซ‡เชŸเชพเชฌเซ‡เช เช“เชฌเซเชœเซ‡เช•เซเชŸเซ‹ เชฌเชจเชพเชตเซ€ เชฐเชนเซเชฏเชพ เช›เซ‡

CREATE ROLE store;
CREATE SCHEMA store AUTHORIZATION store;
CREATE TABLE store.docs
(
  id integer ,         --id ะดะพะบัƒะผะตะฝั‚ะฐ
  man_id integer , --id ะผะตะฝะตะดะถะตั€ะฐ ะดะพะบัƒะผะตะฝั‚ะฐ
  stat_id integer ,  --id ัั‚ะฐั‚ัƒัะฐ ะดะพะบัƒะผะตะฝั‚ะฐ
  ...
  is_del BOOLEAN DEFAULT FALSE 
);
ALTER TABLE store.docs ADD CONSTRAINT doc_pk PRIMARY KEY (id);
ALTER TABLE store.docs OWNER TO store ;

RLS เชจเซ‡ เช…เชฎเชฒเชฎเชพเช‚ เชฎเซ‚เช•เชตเชพ เชฎเชพเชŸเซ‡ เช•เชพเชฐเซเชฏเซ‹ เชฌเชจเชพเชตเชตเชพ

SELECT เชชเช‚เช•เซเชคเชฟเช“ เช•เชฐเชตเชพเชจเซ€ เช•เซเชทเชฎเชคเชพ เชคเชชเชพเชธเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡

เชšเซ‡เช•_เชชเชธเช‚เชฆ เช•เชฐเซ‹

CREATE OR REPLACE FUNCTION store.check_select ( current_id store.docs.id%TYPE ) RETURNS boolean AS $$
DECLARE
  result boolean ;
  curr_pid integer ;
  curr_stat_id integer ;
  doc_man_id integer ;
BEGIN 
  -- DBA ะธะผะตะตั‚ ะดะพัั‚ัƒะฟ ะบะพ ะฒัะตะผ ะดะพะบัƒะผะตะฝั‚ะฐะผ
  IF SESSION_USER = 'curr_dba'
  THEN
    RETURN TRUE ;
  END IF ;
  --------------------------------

  --ะ•ัะปะธ ะดะพะบัƒะผะตะฝั‚ ะธะผะตะตั‚ ะผะตั‚ะบัƒ 'ัƒะดะฐะปะตะฝ' - ะฝะต ะฟะพะบะฐะทั‹ะฒะฐั‚ัŒ ะฒ ะฒั‹ะฑะพั€ะบะต
  SELECT
    is_del
  INTO
    result
  FROM
    store.docs
  WHERE
    id = current_id ;
 IF result = TRUE
 THEN
   RETURN FALSE ;
 END IF ;
 --------------------------------

 --ะŸะพะปัƒั‡ะธั‚ัŒ id ั‚ะตะบัƒั‰ะตะณะพ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั
 SELECT
   service_function.get_curr_pid ()
 INTO
   curr_pid ;
 --------------------------------

 --ะŸะพะปัƒั‡ะธั‚ัŒ id ะผะตะฝะตะดะถะตั€ะฐ ะดะพะบัƒะผะตะฝั‚ะฐ
 SELECT
   man_id
 INTO
   doc_man_id
 FROM
   store.docs
 WHERE
   id = current_id ;
 --------------------------------

 --ะ•ัะปะธ ะผะตะฝะตะดะถะตั€ ะดะพะบัƒะผะตะฝั‚ะฐ ะฝะต ั‚ะตะบัƒั‰ะธะน ะฟะพะปัŒะทะพะฒะฐั‚ะตะปัŒ ะธะปะธ ะผะตะฝะตะดะถะตั€ ะฝะต ะฝะฐะทะฝะฐั‡ะตะฝ
 --ะดะพะฑะฐะฒะธั‚ัŒ ะดะพะบัƒะผะตะฝั‚ ะฒ ะฒั‹ะฑะพั€ะบัƒ
 IF doc_man_id != curr_pid OR doc_man_id IS NULL
 THEN
   RETURN TRUE  ;
 ELSE
   --ะŸะพะปัƒั‡ะธั‚ัŒ ั‚ะตะบัƒั‰ะธะน ัั‚ะฐั‚ัƒั ะดะพะบัƒะผะตะฝั‚ะฐ
   SELECT
     stat_id                                         
   INTO
     curr_statid
   FROM
     store.docs
   WHERE
     id = current_id ;
    
   --ะ•ัะปะธ ัั‚ะฐั‚ัƒั ะฟะพะทะฒะพะปัะตั‚ ะฟั€ะพัะผะพั‚ั€ะตั‚ัŒ ะดะพะบัƒะผะตะฝั‚ - ะดะพะฑะฐะฒะธั‚ัŒ ะดะพะบัƒะผะตะฝั‚ ะฒ ะฒั‹ะฑะพั€ะบัƒ                     
   IF curr_statid = 4 OR curr_statid = 9
   THEN
     RETURN TRUE ;
   ELSE
   --ะ˜ะฝะฐั‡ะต - ะธัะบะปัŽั‡ะธั‚ัŒ ะดะพะบัƒะผะตะฝั‚ ะธะท ะฒั‹ะฑะพั€ะบะธ
     RETURN FALSE ;
    END IF ;
  END IF ;
  --------------------------------

 RETURN FALSE ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.check_select( store.docs.id%TYPE  ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_select( store.docs.id%TYPE  ) FROM public; 
GRANT EXECUTE ON FUNCTION store.check_select( store.docs.id%TYPE  ) TO service_functions; 

เชชเช‚เช•เซเชคเชฟเช“ เชฆเชพเช–เชฒ เช•เชฐเชตเชพเชจเซ€ เช•เซเชทเชฎเชคเชพ เชคเชชเชพเชธเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡

check_insert

CREATE OR REPLACE FUNCTION store.check_insert ( current_id store.docs.id%TYPE ) RETURNS boolean AS $$
DECLARE
  curr_role_id integer ;
BEGIN
  --DBA ะผะพะถะตั‚ ะดะพะฑะฐะฒะปัั‚ัŒ ัั‚ั€ะพะบัƒ ะฒ ะปัŽะฑะพะผ ัะปัƒั‡ะฐะต
  IF SESSION_USER = 'curr_dba'
  THEN
    RETURN TRUE ;
  END IF ;
  --------------------------------

 --ะŸะพะปัƒั‡ะธั‚ัŒ id ั€ะพะปะธ ั‚ะตะบัƒั‰ะตะณะพ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั 
 SELECT
   service_functions.current_rid()
  INTO
    curr_role_id ;
 --------------------------------

--ะ•ัะปะธ ั€ะพะปัŒ ะดะพะฟัƒัะบะฐะตั‚ ะฒะพะทะผะพะถะฝะพัั‚ัŒ ัะพะทะดะฐะฝะธั ะฝะพะฒะพะณะพ ะดะพะบัƒะผะตะฝั‚ะฐ
--ั€ะฐะทั€ะตัˆะธั‚ัŒ
IF curr_role_id = 3 OR curr_role_id = 5     
THEN
  RETURN TRUE ;
END IF ;
--------------------------------
RETURN FALSE  ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.check_insert( store.docs.id%TYPE  ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_insert( store.docs.id%TYPE  ) FROM public;
GRANT EXECUTE ON FUNCTION store.check_insert( store.docs.id%TYPE  ) TO service_functions; 

เชชเช‚เช•เซเชคเชฟ เช•เชพเชขเซ€ เชจเชพเช–เชตเชพเชจเซ€ เช•เซเชทเชฎเชคเชพ เชคเชชเชพเชธเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡

เชšเซ‡เช•_เชกเชฟเชฒเซ€เชŸ เช•เชฐเซ‹

CREATE OR REPLACE FUNCTION store.check_delete ( current_id store.docs.id%TYPE )
RETURNS boolean AS $$
BEGIN  
  --ะขะพะปัŒะบะพ DBA ะผะพะถะตั‚ ัƒะดะฐะปัั‚ัŒ ัั‚ั€ะพะบัƒ 
  IF SESSION_USER = 'curr_dba'
  THEN
    RETURN TRUE ;
  END IF ;
  --------------------------------

  RETURN FALSE ;
END
$$ LANGUAGE plpgsql
SECURITY DEFINER;
ALTER FUNCTION store.check_delete( store.docs.id%TYPE  ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.check_delete( store.docs.id%TYPE  ) FROM public;

เชชเช‚เช•เซเชคเชฟ เช…เชชเชกเซ‡เชŸ เช•เชฐเชตเชพเชจเซ€ เช•เซเชทเชฎเชคเชพ เชคเชชเชพเชธเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡.

เช…เชชเชกเซ‡เชŸ_เช‰เชชเชฏเซ‹เช—

CREATE OR REPLACE FUNCTION store.update_using ( current_id store.docs.id%TYPE , is_del boolean  )
RETURNS boolean AS $$
BEGIN  
   --ะ”ะพะบัƒะผะตะฝั‚ั‹ ะธะผะตัŽั‰ะธะต ัั‚ะฐั‚ัƒั 'ัƒะดะฐะปะตะฝ' - ะฝะต ั€ะตะดะฐะบั‚ะธั€ัƒัŽั‚ัั
   IF is_del 
   THEN
     RETURN FALSE ;
 ELSE
    RETURN TRUE ;
  END IF ;

END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.update_using(  store.docs.id%TYPE ,  boolean  ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.update_using(  store.docs.id%TYPE ,  boolean  ) FROM public;
GRANT EXECUTE ON FUNCTION store.update_using( store.docs.id%TYPE  ) TO service_functions;

เช…เชชเชกเซ‡เชŸ_เชšเซ‡เช•

CREATE OR REPLACE FUNCTION store.update_with_check ( current_id store.docs.id%TYPE , is_del boolean )
RETURNS boolean AS $$
DECLARE
  current_rid integer ;
  current_statid integer ;
BEGIN                

  --DBA ะผะพะถะตั‚ ะฟั€ะพัะผะฐั‚ั€ะธะฒะฐั‚ัŒ ัั‚ั€ะพะบัƒ 
  IF SESSION_USER = 'curr_dba'
  THEN
    RETURN TRUE ;
  END IF ;
  --------------------------------

 --ะŸะพะปัƒั‡ะธั‚ัŒ id ั€ะพะปะธ ั‚ะตะบัƒั‰ะตะณะพ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั 
 SELECT
   service_functions.current_rid()
  INTO
    curr_role_id ;
 --------------------------------                            

 --ะฃะดะฐะปะตะฝะธะต ะดะพะบัƒะผะตะฝั‚ะฐ - ะธะทะผะตะฝะตะฝะธะต ะฟั€ะธะทะฝะฐะบะฐ 
 IF is_deleted
 THEN
   --ะ•ัะปะธ ั€ะพะปัŒ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั ***
   IF current_role_id = 3        
   THEN
      SELECT
        stat_id                                          
      INTO
        curr_statid
      FROM
        store.docs
      WHERE
        id = current_id ;

      --ะ”ะพะบัƒะผะตะฝั‚ ะฒ ัั‚ะฐั‚ัƒัะต *** ะฝะตะปัŒะทั ัƒะดะฐะปะธั‚ัŒ 
      IF current_status_id = 11
      THEN
         RETURN FALSE ;
      ELSE
      --ะœะพะถะฝะพ ัƒะดะฐะปะธั‚ัŒ ะดะพะบัƒะผะตะฝั‚ ะฒ ะดั€ัƒะณะธั… ัั‚ะฐั‚ัƒัะฐั…
        RETURN TRUE ;
      END IF ;

    --ะ˜ะฝะฐั‡ะต , ะตัะปะธ ั€ะพะปัŒ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั ***
    ELSIF current_role_id = 5            
    THEN
      --ะ’ัะต ัั‚ะฐั‚ัƒัั‹ ะดะพะบัƒะผะตะฝั‚ะฐ 
      RETURN TRUE ;
    ELSE
      --ะ”ั€ัƒะณะธะต ะฟะพะปัŒะทะพะฒะฐั‚ะตะปะธ ะฝะต ะผะพะณัƒั‚ ัƒะดะฐะปัั‚ัŒ ะดะพะบัƒะผะตะฝั‚ั‹
      RETURN FALSE ;
    END IF ;
 ELSE      
   --ะžะฑะฝะพะฒะปะตะฝะธะต ะดะพะบัƒะผะตะฝั‚ะฐ ั€ะฐะทั€ะตัˆะตะฝะพ
    RETURN TRUE ;
END IF ;

RETURN FALSE ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
ALTER FUNCTION store.update_with_check( storg.docs.id%TYPE ,  boolean   ) OWNER TO store ;
REVOKE EXECUTE ON FUNCTION store.update_with_check( storg.docs.id%TYPE ,  boolean   )  FROM public;
GRANT EXECUTE ON FUNCTION store.update_with_check( store.docs.id%TYPE  ) TO service_functions;

เชŸเซ‡เชฌเชฒ เชฎเชพเชŸเซ‡ เชฐเซ‹ เชฒเซ‡เชตเชฒ เชธเซ‡เช•เซเชฏเซเชŸเชฐเซ€ เชชเซ‹เชฒเชฟเชธเซ€เชจเซ‡ เชธเช•เซเชทเชฎ เช•เชฐเซ‹.

เชชเช‚เช•เซเชคเชฟ เชธเซเชคเชฐเชจเซ€ เชธเซเชฐเช•เซเชทเชพเชจเซ‡ เชธเช•เซเชทเชฎ เช•เชฐเซ‹

ALTER TABLE store.docs ENABLE ROW LEVEL SECURITY ;

CREATE POLICY doc_select ON store.docs FOR SELECT TO service_functions USING ( (SELECT store.check_select(id)) );
CREATE POLICY doc_insert ON store.docs FOR INSERT TO service_functions WITH CHECK ( (SELECT store.check_insert(id)) );
CREATE POLICY docs_delete ON store.docs FOR DELETE TO service_functions USING ( (SELECT store.check_delete(id)) );

CREATE POLICY doc_update_using ON store.docs FOR UPDATE TO service_functions USING ( (SELECT store.update_using(id , is_del )) );
CREATE POLICY doc_update_check ON store.docs FOR UPDATE TO service_functions  WITH CHECK ( (SELECT store.update_with_check(id , is_del )) );

เชชเชฐเชฟเชฃเชพเชฎ

เชคเซ‡ เช•เชพเชฎ เช•เชฐเซ‡ เช›เซ‡.

เชธเซ‚เชšเชฟเชค เชตเซเชฏเซ‚เชนเชฐเชšเชจเชพเชฅเซ€ เชฐเซ‹เชฒ เชฎเซ‹เชกเซ‡เชฒเชจเชพ เช…เชฎเชฒเซ€เช•เชฐเชฃเชจเซ‡ เชตเซเชฏเชตเชธเชพเชฏเชฟเช• เช•เชพเชฐเซเชฏเซ‹เชจเชพ เชธเซเชคเชฐเชฅเซ€ เชกเซ‡เชŸเชพ เชธเซเชŸเซ‹เชฐเซ‡เชœ เชธเซเชคเชฐ เชชเชฐ เชธเซเชฅเชพเชจเชพเช‚เชคเชฐเชฟเชค เช•เชฐเชตเชพเชจเซเช‚ เชถเช•เซเชฏ เชฌเชจเซเชฏเซเช‚.

เชœเซ‹ เชตเซเชฏเชตเชธเชพเชฏเชฟเช• เช†เชตเชถเซเชฏเช•เชคเชพเช“เชจเซ‡ เชคเซ‡เชจเซ€ เชœเชฐเซ‚เชฐ เชนเซ‹เชฏ เชคเซ‹ เชตเชงเซ เช…เชคเซเชฏเชพเชงเซเชจเชฟเช• เชกเซ‡เชŸเชพ เช›เซเชชเชพเชตเชตเชพเชจเชพ เชฎเซ‹เชกเชฒเซเชธเชจเซ‡ เช…เชฎเชฒเชฎเชพเช‚ เชฎเซ‚เช•เชตเชพ เชฎเชพเชŸเซ‡ เชซเช‚เช•เซเชถเชจเชจเซ‹ เช‰เชชเชฏเซ‹เช— เชŸเซ‡เชฎเซเชชเชฒเซ‡เชŸ เชคเชฐเซ€เช•เซ‡ เชฅเชˆ เชถเช•เซ‡ เช›เซ‡.

เชธเซ‹เชฐเซเชธ: www.habr.com

เชเช• เชŸเชฟเชชเซเชชเชฃเซ€ เช‰เชฎเซ‡เชฐเซ‹