diff --git a/banque.correction.sql b/banque.correction.sql index 6f08e3d..580d921 100644 --- a/banque.correction.sql +++ b/banque.correction.sql @@ -1,4 +1,3 @@ drop table if exists eurofxref; - - +SELECT rand_account(2); \ No newline at end of file diff --git a/banque.md b/banque.md index 09cc7a7..fea098d 100644 --- a/banque.md +++ b/banque.md @@ -25,12 +25,12 @@ Pour les entités vous utiliserez le singuler et écrirez le tout en minuscule. - Séance 1 : [Le schéma Entités-Relations](banque.erd.md) - Séance 2 : - - [Implémentation du modèle](banques.tables.md) - - [Les procédures](banques.procedures.md) + - [Implémentation du modèle](banque.tables.md) + - [Les procédures](banque.procedures.md) - Séance 3 : - - [Les vues](banques.vues.md) - - [Les fonctions](banques.functions.md) + - [Les vues](banque.vues.md) + - [Les fonctions](banque.functions.md) Voir les adresses des serveurs [postgreSQL](https://sources.neotech.fr/Universite/tp/src/branch/main/geii3_2025.md) diff --git a/banque/banque.1.tables.sql b/banque/banque.1.tables.sql index 6d6d48e..695dc8c 100644 --- a/banque/banque.1.tables.sql +++ b/banque/banque.1.tables.sql @@ -1,6 +1,9 @@ drop schema if exists public cascade; create schema public; +create schema if not exists ext; +create extension if not exists pgcrypto schema ext; + create type holder_type as enum ('BANK', 'PERSON', 'COMPANY'); create table holder ( @@ -71,8 +74,10 @@ create table exchange_rate ( primary key (currency_code, date) ); -create temporary table eurofxref ( - "date" date, +drop table if exists eurofxref; + +create temporary table if not exists eurofxref ( + "date" date primary key, USD decimal, JPY decimal, BGN text, CYP text, CZK decimal, DKK decimal, EEK text, GBP decimal, HUF decimal, LTL text, LVL text, MTL text, PLN decimal, ROL text, RON text, @@ -114,21 +119,56 @@ select rate limit 1; $$; -/* - * Account - * La contrainte check (balance >= 0) empêche les soldes négatifs - */ +-- Jeu de caractères utilisé +CREATE OR REPLACE FUNCTION chars36() +RETURNS text AS $$ + SELECT '12345ABCDE'; +$$ LANGUAGE sql IMMUTABLE; + +CREATE OR REPLACE FUNCTION char_to_int(c text) +RETURNS int AS $$ +DECLARE + pos int; +BEGIN + pos := position(c IN chars36()) - 1; + IF pos < 0 THEN + RAISE EXCEPTION 'Invalid base36 character: %', c; + END IF; + RETURN pos; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION compute_checksum(code5 text) +RETURNS text AS $$ +DECLARE + chars text := chars36(); + weights int[] := ARRAY[7, 3, 1, 9, 11, 5]; + total int := 0; + c text; + i int; +BEGIN + + FOR i IN 1..length(code5) LOOP + c := substr(code5, i, 1); + total := total + char_to_int(c) * weights[i]; + END LOOP; + + total := total % length(chars); + RETURN substr(chars, total + 1, 1); +END; +$$ LANGUAGE plpgsql IMMUTABLE; CREATE OR REPLACE FUNCTION rand_account(n integer) RETURNS text LANGUAGE plpgsql AS $$ DECLARE - chars text := '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + chars text := chars36(); out text := ''; - b bytea := gen_random_bytes(n); -- n octets aléatoires + b bytea := ext.gen_random_bytes(n); -- n octets aléatoires i int; idx int; + check_char text; begin IF n <= 0 THEN RAISE EXCEPTION 'La longueur doit être > 0'; @@ -139,10 +179,45 @@ begin out := out || substr(chars, idx, 1); END LOOP; - RETURN out; -END; + RETURN out || compute_checksum(out); +end; $$; + +CREATE OR REPLACE FUNCTION is_valid_account_number(code text) +RETURNS boolean AS $$ +DECLARE + base5 text; + given_checksum text; + expected_checksum text; +BEGIN + -- 1. vérification format + IF code IS NULL OR length(code) <> 6 THEN + RETURN false; + END IF; + + -- 2. séparation + base5 := upper(substr(code, 1, 5)); -- normalisation + given_checksum := upper(substr(code, 6, 1)); + + -- 3. validation caractères autorisés + IF base5 ~ '[^0-9A-Z]' OR given_checksum ~ '[^0-9A-Z]' THEN + RETURN false; + END IF; + + -- 4. recalcul de la vraie clé de contrôle + expected_checksum := compute_checksum36(base5); + + -- 5. comparaison + RETURN expected_checksum = given_checksum; +END; +$$ LANGUAGE plpgsql; + +/* + * Account + * La contrainte check (balance >= 0) empêche les soldes négatifs + */ + create table account ( "id" bigint primary key generated always as identity, "opened_at" date not null default current_date, diff --git a/compose.yaml b/compose.yaml index 42bc114..5bad5a7 100644 --- a/compose.yaml +++ b/compose.yaml @@ -128,54 +128,7 @@ services: # Web API # # ---------------------------------------------------------------------- -# PostgREST -# Serve a fully RESTful API from any existing PostgreSQL database. -# It provides a cleaner, more standards-compliant, faster API than you are likely to write from scratch. -# https://docs.postgrest.org/en/v13/ - postgrest: - image: postgrest/postgrest:v13.0.7 - restart: "no" - depends_on: - database: - condition: service_healthy - caddy: - condition: service_started - command: postgrest - environment: - PGRST_DB_URI: postgres://${POSTGREST_USER:-postgrest}:${POSTGREST_PASSWORD}@${POSTGREST_HOST:-database}:${POSTGREST_DB_PORT:-5432}/${COMPOSE_PROJECT_NAME} - PGRST_DB_SCHEMAS: ${POSTGREST_DB_SCHEMAS:-public} - PGRST_DB_ANON_ROLE: ${POSTGREST_DB_ANON_ROLE:-anonyme} - PGRST_JWT_SECRET: ${POSTGREST_JWT_SECRET:-ChangeMeChangeMeChangeMeChangeMe} - PGRST_ADMIN_SERVER_PORT: 3055 - PGRST_SERVER_PORT: 80 - PGRST_OPENAPI_SERVER_PROXY_URI: https://postgrest.localhost - networks: - - caddy_net - labels: - caddy: postgrest.localhost - caddy.reverse_proxy: "{{upstreams 80}}" - caddy.tls: internal -# Scalar -# Create world-class API Docs with a built-in interactive playground -# which seamlessly turns to a full featured API Client - scalar: - image: scalarapi/api-reference:0.4.2 - restart: "no" - environment: - API_REFERENCE_CONFIG: | - { - "sources":[ - { "url": "https://postgrest.localhost" } - ], - "theme": "purple" - } - networks: - - caddy_net - labels: - caddy: scalar.localhost - caddy.reverse_proxy: "{{upstreams 8080}}" - caddy.tls: internal