C’è un modo per disabilitare l’overloading delle funzioni in Postgres

I miei utenti e io non usano l’overloading di funzioni in PL / pgSQL. Abbiamo sempre una funzione per (schema, nome) tupla. Pertanto, vorremmo rilasciare una funzione solo per nome, cambiarne la firma senza doverla prima rilasciare, ecc. Si consideri ad esempio la seguente funzione:

CREATE OR REPLACE FUNCTION myfunc(day_number SMALLINT) RETURNS TABLE(a INT) AS $BODY$ BEGIN RETURN QUERY (SELECT 1 AS a); END; $BODY$ LANGUAGE plpgsql; 

Per risparmiare tempo, vorremmo invocarlo come segue, senza qualificare 1 con ::SMALLINT , perché esiste solo una funzione denominata myfunc e ha esattamente un parametro denominato day_number:

 SELECT * FROM myfunc(day_number := 1) 

Non c’è ambiguità e il valore 1 è coerente con il tipo SMALLINT , tuttavia PostgreSQL si lamenta:

 SELECT * FROM myfunc(day_number := 1); 
 ERROR: function myfunc(day_number := integer) does not exist LINE 12: SELECT * FROM myfunc(day_number := 1); ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. 

Quando invochiamo tali funzioni da Python, usiamo un wrapper che cerca le firme delle funzioni e qualifica i parametri con i tipi. Questo approccio funziona, ma sembra che ci sia un potenziale di miglioramento.

C’è un modo per distriggersre del tutto l’overloading delle funzioni?

Questo in realtà non è direttamente una questione di sovraccarico delle funzioni (che sarebbe imansible “spegnere”). È una questione di risoluzione del tipo di funzione . (Naturalmente, questo algoritmo potrebbe essere più permissivo senza funzioni sovraccariche.)

Tutto ciò funzionerebbe solo:

 SELECT * FROM myfunc(day_number := ' 1 ' ); SELECT * FROM myfunc( ' 1 ' ); -- note the quotes SELECT * FROM myfunc(1::smallint); SELECT * FROM myfunc('1'::smallint); 

Perché?

Gli ultimi due sono piuttosto ovvi, hai già accennato alla tua domanda.
I primi due sono più interessanti, la spiegazione è sepolta nella risoluzione del tipo di funzione :

si presume che i letterali sconosciuti siano convertibili a qualsiasi cosa per questo scopo.

E questa dovrebbe essere la soluzione semplice per te: usa stringhe letterali .

Un letterale non tipizzato ( '1' , con virgolette) o “string letterale” come definito nello standard SQL è di natura diversa da qualsiasi altro letterale tipizzato (o costante).

Una costante numerica ( 1 , senza virgolette) viene immediatamente convertita in un tipo numerico. Per documentazione:

Una costante numerica che non contiene né un punto decimale né un esponente si presume che sia di tipo intero se il suo valore si adatta al integer (32 bit); altrimenti si presume che sia di tipo bigint se il suo valore si adatta al tipo bigint (64 bit); altrimenti è considerato di tipo numeric . Le costanti che contengono punti decimali e / o esponenti si presumono sempre inizialmente come tipo numeric .

Il tipo di dati inizialmente assegnato di una costante numerica è solo un punto di partenza per gli algoritmi di risoluzione del tipo. Nella maggior parte dei casi, la costante verrà automaticamente convertita nel tipo più appropriato in base al contesto. Se necessario, puoi forzare un valore numerico ad essere interpretato come un tipo di dati specifico lanciandolo.

Grassetto enfasi mio.

L’assegnazione nella funzione call ( day_number := 1 ) è un caso speciale, il tipo di dati di day_number è sconosciuto a questo punto. Postgres non può ricavare un tipo di dati da questo incarico e il valore predefinito è integer .

Di conseguenza, Postgres cerca una funzione che prenda per primo un integer . Quindi, per le funzioni che accettano un tipo solo un cast implicito dall’intero, in altre parole:

 SELECT casttarget::regtype FROM pg_cast WHERE castsource = 'int'::regtype AND castcontext = 'i'; 

Verrebbero trovati tutti e conflitti se c’erano più di una funzione. Sarebbe sovraccarico di funzioni e si otterrebbe un messaggio di errore diverso. Con due funzioni candidate come questa:

 SELECT * FROM myfunc(1); 
 ERROR: function myfunc(integer) is not unique 

Nota il “numero intero” nel messaggio: la costante numerica è stata convertita in integer .

Tuttavia, il cast dal integer al smallint è “solo” un cast assegnato . Ed è qui che finisce il viaggio:

 No function matches the given name and argument types. 

SQL Fiddle.

Spiegazione più dettagliata in queste risposte correlate:

  • ERRORE PostgreSQL: la funzione to_tsvector (carattere che varia, sconosciuto) non esiste

  • Genera serie di date – utilizzando il tipo di data come input

Correzione sporca

Puoi risolvere questo problema “aggiornando” il cast da integer a smallint a un cast implicito :

 UPDATE pg_cast SET castcontext = 'i' WHERE castsource = 'int'::regtype AND casttarget = 'int2'::regtype; 

Ma sconsiglio vivamente di manomettere il sistema di casting predefinito. Consideralo solo se sai esattamente cosa stai facendo. Troverai discussioni correlate nelle liste di Postgres. Può avere tutti i tipi di effetti collaterali, a partire dalla risoluzione del tipo di funzione, ma non finisce qui.

A parte

La risoluzione del tipo di funzione è completamente indipendente dalla lingua utilizzata. Una funzione SQL potrebbe competere con le funzioni PL / perl o PL / pgSQL o “interne” allo stesso modo. La firma della funzione è essenziale. Le funzioni built-in vengono prima di tutto, perché pg_catalog viene prima nel percorso di ricerca predefinito.

Erwin ha inviato una risposta corretta. La mia prossima risposta è legata alla possibilità di disabilitare il sovraccarico.

Non è ansible disabilitare il sovraccarico – questa è una funzionalità di base del sistema API della funzione PostgreSQL – e non può essere distriggersta. Sappiamo quindi che ci sono alcuni effetti collaterali come la rigidità della firma di una funzione forte, ma è la protezione contro alcuni spiacevoli effetti collaterali quando la funzione è usata in Views, definizioni di tabelle, .. Quindi non è ansible disabilitarla.

Puoi semplicemente controllare se hai o non hai sovraccaricato le funzioni:

 postgres=# select count(*), proname from pg_proc where pronamespace <> 11 group by proname having count(*) > 1; count | proname -------+--------- (0 rows) 

Ci sono un sacco di funzioni integrate che sono sovraccariche, quindi semplicemente non funzionerebbe se si distriggerssse l’overloading delle funzioni.