My first PL/pgSQL function

From: Dane Foster <studdugie(at)gmail(dot)com>
To: pgsql-general <pgsql-general(at)postgresql(dot)org>
Subject: My first PL/pgSQL function
Date: 2015-10-20 14:45:44
Message-ID: CA+WxinKj1zM5god0JFmy-C1fuX65MntUVGEemyQFp9peFybW1A@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

Hello,

I'm in the very very very very early stages of migrating a MySQL/PHP app to
PostgreSQL/PHP/Lua. Because we are moving to PostgreSQL one of the [many]
things I intend to change is to move ALL the SQL code/logic out of the
application layer and into the database where it belongs. So after months
of reading the [fine] PostgreSQL manual my first experiment is to port some
PHP/SQL code to a PostgreSQL function.

At this stage the function is a purely academic exercise because like I
said before it's early days so no data has been migrated yet so I don't
have data to test it against. My reason for sharing at such an early stage
is because all I've done so far is read the [fine] manual and I'd like to
know if I've groked at least some of the material.

I would appreciate any feedback you can provide. I am particularly
interested in learning about the most efficient way to do things in
PL/pgSQL because I would hate for the first iteration of the new version of
the app to be slower than the old version.

Thank you for your consideration,

Dane

​/**
* Returns the status of a coupon or voucher.
* @param _code The discount code.
* @return NULL if the discount does not exist otherwise a JSON object.
*
* Voucher codes have the following properties:
* type - The type of discount (voucher, giftcert).
*
* status - The status of the voucher. The valid values are:
* void - The voucher has been voided.
*
* expired - The voucher has expired.
*
* inactive - The gift certificate has not been sent yet.
*
* ok - The voucher has been activated, has not expired,
and has a
* current value greater than zero.
*
* date - The expiration or activation or void date of the voucher in a
reader
* friendly format.
*
* datetime - The expiration or activation or void date of the gift
certificate in
* YYYY-MM-DD HH:MM:SS format.
*
* value - The current value of the voucher.
*
* The mandatory properties are type and status. The presence of the other
properties
* are dependent on the value of status.
************************************************************************************
* Coupon codes can provide the following additional parameters that are
used to
* determine if an order meets a coupon's minimum requirements.
* @param int seats The number of seats in the user's cart.
* @param numeric subtotal The order's subtotal.
*
* Coupon codes have the following properties:
* type - The type of discount (coupon).
*
* status - The status of the coupon code. The valid values are:
* void - The coupon has been voided.
*
* expired - The coupon has expired.
*
* inactive - The coupon has not been activated yet.
*
* min - The minimum seats or dollar amount requirement has
not been
* met.
*
* ok - The coupon can be used.
*
* min - The minimum seats or dollar amount requirement. The value of
this
* property is either an unsigned integer or dollar amount
string w/ the
* dollar sign.
*
* date - The expiration or activation or void date of the coupon in a
reader
* friendly format.
*
* datetime - The expiration or activation or void date of the coupon in
YYYY-MM-DD
* HH:MM:SS format.
*
* value - The current value of the coupon as a string. The value of
this property
* is either an unsigned integer w/ a percent symbol or dollar
amount
* string w/ the dollar sign.
*/
CREATE OR REPLACE FUNCTION check_discount_code(
_code public.CITXT70,
VARIADIC cpnxtra NUMERIC[]
) RETURNS JSON AS $$
DECLARE
discount RECORD;
BEGIN

SELECT
ok,
v.value,
created,
expires,
modified,
effective_date,
-- The minimum quantity or dollar amount required to use the coupon.
COALESCE(
lower(qty_range),
'$' || to_char(lower(amount_range), '999999999999999D99')
) AS min,
CASE type::TEXT
WHEN 'voucher'
THEN
CASE WHEN gd.code IS NOT NULL THEN 'giftcert' END
ELSE type::TEXT
END AS type,
to_char(expires, 'Dy, MM Mon. YYYY') AS expd,
to_char(modified, 'Dy, MM Mon. YYYY') AS mdate,
to_char(effective_date, 'Dy, MM Mon. YYYY') AS edate,
-- Determines if the coupon has been used up.
CASE WHEN maxuse > 0 THEN maxuse - used <= 0 ELSE FALSE END AS maxuse,
effective_date > CURRENT_DATE AS notyet,
expires < CURRENT_DATE AS expired,
-- The coupon's discount value as a dollar amount or percent.
COALESCE(
discount_rate || '%',
'$' || to_char(discount_amount, '999999999999999D99')
) AS discount,
cpn.code IS NULL AS
danglingcoupon,
v.code IS NULL AS
danglingvoucher
INTO STRICT discount
FROM
discount_codes AS dc
LEFT JOIN coupons AS cpn USING (code)
LEFT JOIN vouchers AS v USING (code)
LEFT JOIN giftcerts_d AS gd USING (code)
WHERE
dc.code = _code;

IF FOUND THEN
CASE discount.type
WHEN 'coupon'
THEN
IF discount.danglingcoupon
THEN
-- This should NEVER happen!
DELETE FROM discount_codes WHERE code = _code;
RAISE WARNING 'Removed dangling coupon code: %', _code;
ELSE
IF discount.maxuse OR NOT discount.ok
THEN
RETURN json_build_object('status', 'void', 'type', 'coupon');
END IF;

IF discount.expired
THEN
RETURN json_build_object(
'type', 'coupon',
'status', 'expired',
'date', discount.expd,
'datetime', discount.expires
);
END IF;

IF discount.notyet THEN
RETURN json_build_object(
'type', 'coupon',
'date', discount.edate,
'status', 'inactive',
'datetime', discount.effective_date
);
END IF;

IF 2 = array_length(cpnxtra, 1)
THEN
IF discount.min IS NOT NULL
THEN
-- @TODO - Test the regex to ensure it is escaped properly.
IF discount.min ~ '^\$'
THEN
IF right(discount.min, -1)::NUMERIC > cpnxtra[1]::NUMERIC
THEN
RETURN json_build_object(
'status', 'min',
'type', 'coupon',
'min', discount.min
);
END IF;
ELSIF discount.min::INT > cpnxtra[0]::INT
THEN
RETURN json_build_object(
'status', 'min',
'type', 'coupon',
'min', discount.min
);
END IF;

RETURN json_build_object(
'status', 'ok',
'type', 'coupon',
'min', discount.min,
'value', discount.discount
);
END IF;
END IF;

RETURN json_build_object(
'status', 'ok',
'type', 'coupon',
'value', discount.discount
);
END IF;
ELSE
IF discount.danglingvoucher
THEN
-- This should NEVER happen!
DELETE FROM discount_codes WHERE code = _code;
RAISE WARNING 'Removed dangling voucher: %', _code;
ELSE
IF NOT discount.ok
THEN
RETURN json_build_object(
'status', 'void',
'type', discount.type,
'date', discount.mdate,
'datetime', discount.modified
);
END IF;

IF discount.expired
THEN
RETURN json_build_object(
'status', 'expired',
'type', discount.type,
'date', discount.expd,
'datetime', discount.expires
);
END IF;

IF discount.notyet
THEN
RETURN json_build_object(
'type', discount.type,
'date', discount.edate,
'status', 'inactive',
'datetime', discount.effective_date,
'value', to_char(discount.value, '999999999999999D99')
);
END IF;

IF discount.value > 0
THEN
RETURN json_build_object(
'status', 'ok',
'type', discount.type,
'date', discount.expd,
'datetime', discount.expires,
'value', to_char(discount.value, '999999999999999D99')
);
END IF;

RETURN json_build_object('status', 'depleted', 'type',
discount.type);
END IF;
END CASE;
END IF;

RETURN NULL;

END;
$$ LANGUAGE plpgsql STRICT;

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Tomas Vondra 2015-10-20 15:14:06 Re: RAID and SSD configuration question
Previous Message Scott Marlowe 2015-10-20 14:33:42 Re: RAID and SSD configuration question