/* pgmp -- Module installation SQL script * * Copyright (C) 2011 Daniele Varrazzo * * This file is part of the PostgreSQL GMP Module * * The PostgreSQL GMP Module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * The PostgreSQL GMP Module is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the PostgreSQL GMP Module. If not, see * http://www.gnu.org/licenses/. */ !# This file is made of a mix of Python code and SQL statements. !# Use the script ``tools/unmix.py`` to convert it into plain SQL. !! PYON base_type = 'mpz' def func(sqlname, argin, argout=None, cname=None, volatile=False, strict=True): """Create a SQL function from a C function""" if not argout: argout = base_type print "CREATE OR REPLACE FUNCTION %s(%s)" \ % (sqlname, ", ".join(argin.split())) print "RETURNS", argout if not cname: pre = base_type + '_' cname = (sqlname.startswith(pre) and "p" + sqlname or 'p' + pre + sqlname) # not using MODULE_PATHNAME to make the resulting sql file # compatible with both PG pre-9.1 and 9.1 print "AS '$libdir/pgmp', '%s'" % cname print "LANGUAGE C", print volatile and "VOLATILE" or "IMMUTABLE", print strict and "STRICT" or "", print ";" print def func_tuple(sqlname, args, argout=None, cname=None): """Create a SQL function returning many arguments from a C function""" if not argout: argout = base_type print "CREATE OR REPLACE FUNCTION %s(%s)" \ % (sqlname, args) print "RETURNS RECORD" if not cname: pre = base_type + '_' cname = (sqlname.startswith(pre) and "p" + sqlname or 'p' + pre + sqlname) print "AS '$libdir/pgmp', '%s'" % cname print "LANGUAGE C IMMUTABLE STRICT;" print func('gmp_version', '', 'int4', cname='pgmp_gmp_version') !! PYOFF -- -- mpz user-defined type -- !! PYON func('mpz_in', 'cstring', 'mpz') func('mpz_out', 'mpz', 'cstring') !! PYOFF CREATE TYPE mpz ( INPUT = mpz_in , OUTPUT = mpz_out , INTERNALLENGTH = VARIABLE , STORAGE = EXTENDED , CATEGORY = 'N' ); -- Other I/O functions !! PYON func('mpz', 'text int4', 'mpz', cname='pmpz_in_base') func('text', 'mpz int4', 'cstring', cname='pmpz_out_base') !! PYOFF -- -- mpz cast -- !! PYON import re def castfrom(typname, implicit=False): """Create a cast from a different type to `base_type`""" func(base_type, typname, cname="p%s_from_%s" % (base_type, typname)) print "CREATE CAST (%s AS %s)" % (typname, base_type) print "WITH FUNCTION %s(%s)" % (base_type, typname) print {'I': "AS IMPLICIT;", 'A': "AS ASSIGNMENT;", False: ';'}[implicit] print print castfrom('int2', implicit='I') castfrom('int4', implicit='I') castfrom('int8', implicit='I') castfrom('float4', implicit='A') castfrom('float8', implicit='A') castfrom('numeric', implicit='A') def castto(typname, implicit=False): """Create a cast from `base_type` to a different type""" func(typname, base_type, typname, cname="p%s_to_%s" % (base_type, typname)) print "CREATE CAST (%s AS %s)" % (base_type, typname) print "WITH FUNCTION %s(%s)" % (typname, base_type) print {'I': "AS IMPLICIT;", 'A': "AS ASSIGNMENT;", False: ';'}[implicit] print print castto('int8', implicit='A') castto('int4', implicit='A') castto('int2', implicit='A') castto('float4', implicit='A') castto('float8', implicit='A') !! PYOFF CREATE CAST (mpz AS numeric) WITH INOUT AS ASSIGNMENT; -- -- mpz operators -- !! PYON func('mpz_uplus', 'mpz') func('mpz_neg', 'mpz') func('abs', 'mpz') func('sgn', 'mpz', 'int4') func('even', 'mpz', 'bool') func('odd', 'mpz', 'bool') !! PYOFF CREATE OPERATOR - ( RIGHTARG = mpz, PROCEDURE = mpz_neg ); CREATE OPERATOR + ( RIGHTARG = mpz, PROCEDURE = mpz_uplus ); !! PYON def op(sym, fname, rarg=None, comm=None): """Create an operator on `base_type`""" if rarg == None: rarg = base_type func('%s_%s' % (base_type, fname), base_type + " " + rarg) print "CREATE OPERATOR %s (" % sym print " LEFTARG = %s," % base_type print " RIGHTARG = %s," % rarg if comm: print " COMMUTATOR = %s," % comm print " PROCEDURE = %s_%s" % (base_type, fname) print ");" print print op('+', 'add', comm='+') op('-', 'sub') op('*', 'mul', comm='*') op('/', 'tdiv_q') op('%', 'tdiv_r') op('+/', 'cdiv_q') op('+%', 'cdiv_r') op('-/', 'fdiv_q') op('-%', 'fdiv_r') op('/!', 'divexact') op('<<', 'mul_2exp', rarg='int8') op('>>', 'tdiv_q_2exp', rarg='int8') op('%>', 'tdiv_r_2exp', rarg='int8') op('+>>', 'cdiv_q_2exp', rarg='int8') op('+%>', 'cdiv_r_2exp', rarg='int8') op('->>', 'fdiv_q_2exp', rarg='int8') op('-%>', 'fdiv_r_2exp', rarg='int8') func_tuple('tdiv_qr', 'mpz, mpz, out q mpz, out r mpz') func_tuple('cdiv_qr', 'mpz, mpz, out q mpz, out r mpz') func_tuple('fdiv_qr', 'mpz, mpz, out q mpz, out r mpz') func('divisible', 'mpz mpz', 'bool') func('divisible_2exp', 'mpz int8', 'bool') func('congruent', 'mpz mpz mpz', 'bool') func('congruent_2exp', 'mpz mpz int8', 'bool') func('pow', 'mpz int8', cname='pmpz_pow_ui') func('powm', 'mpz mpz mpz') op('&', 'and') op('|', 'ior') op('#', 'xor') func('com', 'mpz', 'mpz') func('popcount', 'mpz', 'mpz') func('hamdist', 'mpz mpz', 'mpz') func('scan0', 'mpz mpz', 'mpz') func('scan1', 'mpz mpz', 'mpz') func('setbit', 'mpz mpz', 'mpz') func('clrbit', 'mpz mpz', 'mpz') func('combit', 'mpz mpz', 'mpz') func('tstbit', 'mpz mpz', 'int4') func('gmp_max_bitcnt', '', 'mpz', cname='pgmp_max_bitcnt') !! PYOFF CREATE OPERATOR /? ( LEFTARG = mpz, RIGHTARG = mpz, PROCEDURE = divisible ); CREATE OPERATOR >>? ( LEFTARG = mpz, RIGHTARG = int8, PROCEDURE = divisible_2exp ); CREATE OPERATOR ^ ( LEFTARG = mpz, RIGHTARG = int8, PROCEDURE = pow ); -- -- mpz comparisons -- CREATE OR REPLACE FUNCTION mpz_eq(mpz, mpz) RETURNS boolean AS '$libdir/pgmp', 'pmpz_eq' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR = ( LEFTARG = mpz , RIGHTARG = mpz , PROCEDURE = mpz_eq , COMMUTATOR = = , NEGATOR = <> , RESTRICT = eqsel , JOIN = eqjoinsel , HASHES , MERGES ); CREATE OR REPLACE FUNCTION mpz_ne(mpz, mpz) RETURNS boolean AS '$libdir/pgmp', 'pmpz_ne' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR <> ( LEFTARG = mpz , RIGHTARG = mpz , PROCEDURE = mpz_ne , COMMUTATOR = <> , NEGATOR = = , RESTRICT = neqsel , JOIN = neqjoinsel ); !! PYON def bop(sym, fname, comm, neg): """Create an operator on `base_type` returning a bool""" func('%s_%s' % (base_type, fname), base_type + " " + base_type, argout='boolean') fname1 = fname[0] + 't' print "CREATE OPERATOR %s (" % sym print " LEFTARG =", base_type print " , RIGHTARG =", base_type print " , PROCEDURE = %s_%s" % (base_type, fname) print " , COMMUTATOR = %s" % comm print " , NEGATOR = %s" % neg print " , RESTRICT = scalar%ssel" % fname1 print " , JOIN = scalar%sjoinsel" % fname1 print ");" print print bop('>', 'gt', comm='<', neg='<=') bop('>=', 'ge', comm='<=', neg='<') bop('<', 'lt', comm='>', neg='>=') bop('<=', 'le', comm='>=', neg='>') !! PYOFF -- -- mpz indexes -- CREATE OR REPLACE FUNCTION mpz_cmp(mpz, mpz) RETURNS integer AS '$libdir/pgmp', 'pmpz_cmp' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS mpz_ops DEFAULT FOR TYPE mpz USING btree AS OPERATOR 1 < , OPERATOR 2 <= , OPERATOR 3 = , OPERATOR 4 >= , OPERATOR 5 > , FUNCTION 1 mpz_cmp(mpz, mpz) ; CREATE OR REPLACE FUNCTION mpz_hash(mpz) RETURNS integer AS '$libdir/pgmp', 'pmpz_hash' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS mpz_ops DEFAULT FOR TYPE mpz USING hash AS OPERATOR 1 = , FUNCTION 1 mpz_hash(mpz) ; -- TODO: OPERATOR FAMILY? -- mpz functions !! PYON func('sqrt', 'mpz', 'mpz') func('root', 'mpz int8', 'mpz') func('perfect_power', 'mpz', 'bool', cname='pmpz_perfect_power') func('perfect_square', 'mpz', 'bool', cname='pmpz_perfect_square') func_tuple('rootrem', 'mpz, int8, out root mpz, out rem mpz') func_tuple('sqrtrem', 'mpz, out root mpz, out rem mpz') !! PYOFF -- -- Number Theoretic Functions -- !! PYON func('probab_prime', 'mpz int4', 'int4', cname='pmpz_probab_prime_p') func('nextprime', 'mpz') func('gcd', 'mpz mpz', 'mpz') func('lcm', 'mpz mpz', 'mpz') func('invert', 'mpz mpz', 'mpz') func('jacobi', 'mpz mpz', 'int4') func('legendre', 'mpz mpz', 'int4') func('kronecker', 'mpz mpz', 'int4') func('remove', 'mpz mpz', 'mpz') func('fac', 'int8', 'mpz', cname='pmpz_fac_ui') func('bin', 'mpz int8', 'mpz', cname='pmpz_bin_ui') func('fib', 'int8', 'mpz', cname='pmpz_fib_ui') func('lucnum', 'int8', 'mpz', cname='pmpz_lucnum_ui') func_tuple('gcdext', 'mpz, mpz, out g mpz, out s mpz, out t mpz') func_tuple('fib2', 'int8, out fn mpz, out fnsub1 mpz', cname='pmpz_fib2_ui') func_tuple('lucnum2', 'int8, out ln mpz, out lnsub1 mpz', cname='pmpz_lucnum2_ui') !! PYOFF -- -- Random numbers -- !! PYON func('randinit', '', 'void', cname='pgmp_randinit_default', volatile=True) func('randinit_mt', '', 'void', cname='pgmp_randinit_mt', volatile=True) func('randinit_lc_2exp', 'mpz int8 int8', 'void', cname='pgmp_randinit_lc_2exp', volatile=True) func('randinit_lc_2exp_size', 'int8', 'void', cname='pgmp_randinit_lc_2exp_size', volatile=True) func('randseed', 'mpz', 'void', cname='pgmp_randseed', volatile=True) func('urandomb', 'int8', 'mpz', volatile=True) func('urandomm', 'mpz', 'mpz', volatile=True) func('rrandomb', 'int8', 'mpz', volatile=True) !! PYOFF -- -- Aggregation functions -- !! PYON def agg(sqlname, argin, sfunc, argout=None, ffunc=None, sortop=None): assert sfunc.startswith('_' + base_type) cname = '_p' + sfunc[1:] func(sfunc, 'internal ' + argin, 'internal', cname=cname, strict=False) if not argout: argout = base_type print "CREATE AGGREGATE %s(%s)\n(" \ % (sqlname, ", ".join(argin.split())) print " SFUNC =", sfunc print " , STYPE = internal" print " , FINALFUNC =", ffunc or "_%s_from_agg" % base_type if sortop: print " , SORTOP =", sortop print ");" print func('_mpz_from_agg', 'internal', 'mpz', cname='_pmpz_from_agg') agg('sum', 'mpz', '_mpz_agg_add') agg('prod', 'mpz', '_mpz_agg_mul') agg('max', 'mpz', '_mpz_agg_max', sortop='>') agg('min', 'mpz', '_mpz_agg_min', sortop='<') agg('bit_and', 'mpz', '_mpz_agg_and') agg('bit_or', 'mpz', '_mpz_agg_ior') agg('bit_xor', 'mpz', '_mpz_agg_xor') !! PYOFF -- -- mpq user-defined type -- !! PYON base_type = 'mpq' func('mpq_in', 'cstring', 'mpq') func('mpq_out', 'mpq', 'cstring') !! PYOFF CREATE TYPE mpq ( INPUT = mpq_in , OUTPUT = mpq_out , INTERNALLENGTH = VARIABLE , STORAGE = EXTENDED , CATEGORY = 'N' ); !! PYON func('mpq', 'text int4', 'mpq', cname='pmpq_in_base') func('text', 'mpq int4', 'cstring', cname='pmpq_out_base') func('mpq', 'mpz mpz', 'mpq', cname='pmpq_mpz_mpz') # Currently not exposing these versions as they have surprising limits # E.g. calling mpq(n, d) with two numeric literal of which one fits # into an int and one doesn't fails to find a best candidate. # So we currently only accept mpq(n, d) if both fit into a type smaller # than numeric (because of the implicit casts int? -> mpz) and require # explicit mpz for bigger numbers. # func('mpq', 'int4 int4', 'mpq', cname='pmpq_int4_int4') # func('mpq', 'numeric numeric', 'mpq', cname='pmpq_numeric_numeric') func('num', 'mpq', 'mpz') func('den', 'mpq', 'mpz') castfrom('int2', implicit='I') castfrom('int4', implicit='I') castfrom('int8', implicit='I') castfrom('float4', implicit='I') castfrom('float8', implicit='I') castfrom('numeric', implicit='I') castfrom('mpz', implicit='I') castto('int2', implicit='A') castto('int4', implicit='A') castto('int8', implicit='A') castto('float4', implicit='A') castto('float8', implicit='A') castto('mpz', implicit='A') !! PYOFF CREATE OR REPLACE FUNCTION mpq_to_numeric(mpq, int4) RETURNS numeric AS '$libdir/pgmp', 'pmpq_to_numeric' LANGUAGE C IMMUTABLE STRICT ; CREATE CAST (mpq AS numeric) WITH FUNCTION mpq_to_numeric(mpq, int4) AS ASSIGNMENT; -- -- mpq operators -- !! PYON func('mpq_uplus', 'mpq') func('mpq_neg', 'mpq') func('abs', 'mpq') func('inv', 'mpq') !! PYOFF CREATE OPERATOR - ( RIGHTARG = mpq, PROCEDURE = mpq_neg ); CREATE OPERATOR + ( RIGHTARG = mpq, PROCEDURE = mpq_uplus ); !! PYON op('+', 'add', comm='+') op('-', 'sub') op('*', 'mul', comm='*') op('/', 'div') op('<<', 'mul_2exp', rarg='int8') op('>>', 'div_2exp', rarg='int8') func('limit_den', 'mpq') func('limit_den', 'mpq mpz') !! PYOFF -- -- mpq comparisons -- CREATE OR REPLACE FUNCTION mpq_eq(mpq, mpq) RETURNS boolean AS '$libdir/pgmp', 'pmpq_eq' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR = ( LEFTARG = mpq , RIGHTARG = mpq , PROCEDURE = mpq_eq , COMMUTATOR = = , NEGATOR = <> , RESTRICT = eqsel , JOIN = eqjoinsel , HASHES , MERGES ); CREATE OR REPLACE FUNCTION mpq_ne(mpq, mpq) RETURNS boolean AS '$libdir/pgmp', 'pmpq_ne' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR <> ( LEFTARG = mpq , RIGHTARG = mpq , PROCEDURE = mpq_ne , COMMUTATOR = <> , NEGATOR = = , RESTRICT = neqsel , JOIN = neqjoinsel ); !! PYON bop('>', 'gt', comm='<', neg='<=') bop('>=', 'ge', comm='<=', neg='<') bop('<', 'lt', comm='>', neg='>=') bop('<=', 'le', comm='>=', neg='>') !! PYOFF -- -- mpq indexes -- CREATE OR REPLACE FUNCTION mpq_cmp(mpq, mpq) RETURNS integer AS '$libdir/pgmp', 'pmpq_cmp' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS mpq_ops DEFAULT FOR TYPE mpq USING btree AS OPERATOR 1 < , OPERATOR 2 <= , OPERATOR 3 = , OPERATOR 4 >= , OPERATOR 5 > , FUNCTION 1 mpq_cmp(mpq, mpq) ; CREATE OR REPLACE FUNCTION mpq_hash(mpq) RETURNS integer AS '$libdir/pgmp', 'pmpq_hash' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS mpq_ops DEFAULT FOR TYPE mpq USING hash AS OPERATOR 1 = , FUNCTION 1 mpq_hash(mpq) ; -- TODO: OPERATOR FAMILY? -- -- Aggregation functions -- !! PYON func('_mpq_from_agg', 'internal', 'mpq', cname='_pmpq_from_agg') agg('sum', 'mpq', '_mpq_agg_add') agg('prod', 'mpq', '_mpq_agg_mul') agg('max', 'mpq', '_mpq_agg_max', sortop='>') agg('min', 'mpq', '_mpq_agg_min', sortop='<') !! PYOFF