Estado: ✅ Aceptada Fecha: 2026-05-29 Bloque: Y5 (cuarto sub-bloque post-Y) Bump on-disk: 20 → 21

🧭 Contexto

Y3 enforce el rango signed para TINYINT/SMALLINT/MEDIUMINT/INT4. Pero la sintaxis MySQL <tipo> UNSIGNED es ultra-común en schemas reales (ID auto-incrementales, contadores, flags) y portar esos schemas a gabysql requería reescribir todo a tipos signed con rangos más amplios.

Y5 cierra esa brecha sin agregar bytes nuevos en disco — reutiliza el byte int_width de Y3 prendiendo el high bit como flag de signedness.

Además agrega gen_random_uuid() (UUID v4 random), la función más pedida después de tipo UUID — sin ella, un schema con id UUID DEFAULT gen_random_uuid() no se puede portar.

💡 Decisión

1. UNSIGNED reusa el byte int_width

int_width byte layout (Y5):
┌──┬──┬──┬──┬──┬──┬──┬──┐
│ S│ 0│ 0│ 0│ W3│W2│W1│W0│
└──┴──┴──┴──┴──┴──┴──┴──┘
 │              └────────── width (1=TINYINT, 2=SMALLINT, 3=MEDIUMINT, 4=INT4, 0=BIGINT default)
 └────────────────────────── sign: 0=signed, 1=unsigned

Ejemplos:

V20 reading V21 con bytes 0x8X los interpretaría como widths inválidos → bump 20→21 obligatorio.

2. Parser: UNSIGNED opcional tras el tipo

parse_column_def agrega un match_keyword("UNSIGNED") después de parse_type_name. Si el tipo no es INT, el flag se ignora silentemente — no es error, sólo no aplica.

3. Rangos con int_width_range

Extendido para chequear el high bit:

match (unsigned, width) {
    (false, 1) => i8 range,
    (true, 1) => (0, u8::MAX),
    (false, 2) => i16 range,
    (true, 2) => (0, u16::MAX),
    (false, 3) => 24-bit signed,
    (true, 3) => (0, 16_777_215),
    (false, 4) => i32 range,
    (true, 4) => (0, u32::MAX),
    (false, _) => (i64::MIN, i64::MAX),
    (true, _) => (0, i64::MAX),  // BIGINT UNSIGNED — limitado por i64 interno
}

4. BIGINT UNSIGNED no llega a u64

El motor sigue usando i64 internamente. BIGINT UNSIGNED enforce sólo >= 0 — el upper bound queda en i64::MAX (9_223_372_036_854_775_807), no u64::MAX. Documentado como limitación.

5. gen_random_uuid() / uuid_v4()

UUID v4 (random), formato canónico 8-4-4-4-12 lowercase:

SELECT GEN_RANDOM_UUID();   -- '5f8b1c2d-4e7a-4b8c-9d0e-1f2a3b4c5d6e'
SELECT UUID_V4();           -- alias
SELECT UUID_GENERATE_V4();  -- alias (PG)
SELECT RANDOM_UUID();       -- alias

PRNG interno: xorshift64 seeded por SystemTime::nanos XOR un magic constant. No es criptográficamente seguro — alcanza para test data, surrogate keys, IDs no-secretos. Sigue RFC 4122 §4.4 para los nibbles de version (4) y variant (10xx).

Cero deps externas — no agregamos uuid crate. La calidad estadística es suficiente para descubrir colisiones en suites de test (~10^18 valores posibles).

📐 Códigos de error

Reutiliza [GBY-4121] INT_RANGE_EXCEEDED para violaciones de UNSIGNED — el mensaje ahora identifica el tipo como TINYINT UNSIGNED etc.

🧪 Validación

Suite y5_* en tests/integration_test.rs (10 tests):

Suite total: 567/567 pass (cargo test --lib --tests).

🔭 Futuro

📝 Actualización 2026-06-15: el “item grande” (DECIMAL exacto) y UUID v7 ya entregados. Lista anotada abajo.

Lo que queda en familia tipos: