-- Test the fact that the domain constraint is being applied
-- in multiple situations.
DROP DOMAIN IF EXISTS i5 CASCADE;
NOTICE:  type "i5" does not exist, skipping
DROP DOMAIN IF EXISTS i4 CASCADE;
NOTICE:  type "i4" does not exist, skipping
DROP DOMAIN IF EXISTS i3 CASCADE;
NOTICE:  type "i3" does not exist, skipping
DROP DOMAIN IF EXISTS i2 CASCADE;
NOTICE:  type "i2" does not exist, skipping
DROP DOMAIN IF EXISTS i1 CASCADE;
NOTICE:  type "i1" does not exist, skipping
-- No constraint, simple alias.
CREATE DOMAIN i1 AS int4;
-- NOT NULL restriction
CREATE DOMAIN i2 AS i1 NOT NULL;
-- Must be greater than or equal to zero
CREATE DOMAIN i3 AS i2 CHECK (VALUE >= 0);
-- And must be less than or equal to zero(only one possible value)
CREATE DOMAIN i4 AS i3 CHECK (VALUE <= 0);
-- And must not be zero(no possible values)
CREATE DOMAIN i5 AS i4 CHECK (VALUE != 0);
-- The point of these domains is to check that the all the basetype
-- constraints are actually being tested when a check() is performed
CREATE OR REPLACE FUNCTION test_domain_instantiation() RETURNS text LANGUAGE python AS
$python$
import Postgres

# Just pack everything in here.
# It's a bit unittest-ish, but very convenient for the test we're doing.

rt = Postgres.Type(Postgres.CONST['REGTYPEOID'])

# load out of order, just in case the implementation
# has some issue wrt fully resolving supers.
i5 = Postgres.Type(rt('i5'))
i2 = Postgres.Type(rt('i2'))
i1 = Postgres.Type(rt('i1'))
i3 = Postgres.Type(rt('i3'))
i4 = Postgres.Type(rt('i4'))
int4 = Postgres.Type(rt('int4'))

# sanity check; proper inheritance is happening, yes?
domains = [
	(i5,i4),
	(i4,i3),
	(i3,i2),
	(i2,i1),
	(i1,int4),
	(int4,Postgres.Object),
]
for sub,sup in domains:
	assert(issubclass(sub,sup))

data = [
	(int4, [
		# just way too big
		234324234324234324234234324234, -123123223742348742384237832,
		-123, 0, 234,
		None
	]),
	(i1, [
		# ditto
		234324234324234324234234324234, -123123223742348742384237832,
		-123, 0, 234,
		None
	]),
	(i2, [
		234324234324234324234234324234, -123123223742348742384237832,
		-123, 0, 234,
		None,
	]),
	(i3, [
		# fail on -1
		-1, 0, 123,
		None
	]),
	(i4, [
		# fail on -1 and 1234
		-2, 0, 1234,
		None
	]),
	(i5, [
		# fail on -1, 0, and 12345
		-3, 0, 12345,
		None
	]),
]

def main():
	output = "\n"
	for typ, dl in data:
		output += "\n" + typ.__name__ + ":\n"
		for x in dl:
			try:
				with xact():
					r = typ(x)
					typ.check(r)
				output += " success for: '" + str(x) + "' -> '" + str(r) + "' (" + type(r).__name__ + ")\n"
			except Postgres.Exception as e:
				output += " fail for: '" + str(x) + "' [" + str(e.code) + ": " + str(e.message) + "]\n"
			except Exception as e:
				output += " fail for: '" + str(x) + "' [" + str(e) + "]\n"
	return output
$python$;
SELECT test_domain_instantiation();
                                test_domain_instantiation                                
-----------------------------------------------------------------------------------------
 
 
 int4:
  fail for: '234324234324234324234234324234' [Python int too large to convert to C long]
  fail for: '-123123223742348742384237832' [Python int too large to convert to C long]
  success for: '-123' -> '-123' (int4)
  success for: '0' -> '0' (int4)
  success for: '234' -> '234' (int4)
  success for: 'None' -> 'None' (NoneType)
 
 i1:
  fail for: '234324234324234324234234324234' [Python int too large to convert to C long]
  fail for: '-123123223742348742384237832' [Python int too large to convert to C long]
  success for: '-123' -> '-123' (i1)
  success for: '0' -> '0' (i1)
  success for: '234' -> '234' (i1)
  success for: 'None' -> 'None' (NoneType)
 
 i2:
  fail for: '234324234324234324234234324234' [Python int too large to convert to C long]
  fail for: '-123123223742348742384237832' [Python int too large to convert to C long]
  success for: '-123' -> '-123' (i2)
  success for: '0' -> '0' (i2)
  success for: '234' -> '234' (i2)
  fail for: 'None' [23502: domain i2 does not allow null values]
 
 i3:
  fail for: '-1' [23514: value for domain i3 violates check constraint "i3_check"]
  success for: '0' -> '0' (i3)
  success for: '123' -> '123' (i3)
  fail for: 'None' [23502: domain i3 does not allow null values]
 
 i4:
  fail for: '-2' [23514: value for domain i4 violates check constraint "i3_check"]
  success for: '0' -> '0' (i4)
  fail for: '1234' [23514: value for domain i4 violates check constraint "i4_check"]
  fail for: 'None' [23502: domain i4 does not allow null values]
 
 i5:
  fail for: '-3' [23514: value for domain i5 violates check constraint "i3_check"]
  fail for: '0' [23514: value for domain i5 violates check constraint "i5_check"]
  fail for: '12345' [23514: value for domain i5 violates check constraint "i4_check"]
  fail for: 'None' [23502: domain i5 does not allow null values]
 
(1 row)

-- This test is validating that domain casting functionality
-- is available. Specifically, no Postgres CAST is actually
-- being used. Rather, the relationship between the types should
-- be identified and used to merely perform a datumCopy
CREATE OR REPLACE FUNCTION test_domain_casts() RETURNS text LANGUAGE python AS
$python$
import Postgres

# Just pack everything in here.
# It's a bit unittest-ish, but very convenient for the test we're doing.

rt = Postgres.Type(Postgres.CONST['REGTYPEOID'])

int4 = Postgres.Type(rt('int4'))
i1 = Postgres.Type(rt('i1'))
i2 = Postgres.Type(rt('i2'))
i3 = Postgres.Type(rt('i3'))
i4 = Postgres.Type(rt('i4'))
i5 = Postgres.Type(rt('i5'))

data = [
	(int4, [
		int4(-123), int4(0), int4(234), int4(None)
	]),
	(i1, [
		i1(-123), i1(0), i1(234), i1(None)
	]),
	(i2, [
		i2(-123), i2(0), i2(234),
	]),
	(i3, [
		i3(0), i3(123),
	]),
	(i4, [i4(0),]),
	(i5, []),
]

types = (int4, i1, i2, i3, i4, i5)

def main():
	output = "\n"
	for typ, dl in data:
		output += "\n" + typ.__name__ + ":\n"
		for x in dl:
			for desttyp in types:
				try:
					with xact():
						r = desttyp(x)
						desttyp.check(r)
					assert r == x
					output += " success for: '" + str(x) + "' -> (" + desttyp.__name__ + ")\n"
				except Postgres.Exception as e:
					output += " fail for: '" + str(x) + "' -> " + desttyp.__name__ + " [" + str(e.code) + ": " + str(e.message) + "]\n"
				except Exception as e:
					output += " fail for: '" + str(x) + "' -> " + desttyp.__name__ + " [" + str(e) + "]\n"
	return output
$python$;
SELECT test_domain_casts();
                                     test_domain_casts                                     
-------------------------------------------------------------------------------------------
 
 
 int4:
  success for: '-123' -> (int4)
  success for: '-123' -> (i1)
  success for: '-123' -> (i2)
  fail for: '-123' -> i3 [23514: value for domain i3 violates check constraint "i3_check"]
  fail for: '-123' -> i4 [23514: value for domain i4 violates check constraint "i3_check"]
  fail for: '-123' -> i5 [23514: value for domain i5 violates check constraint "i3_check"]
  success for: '0' -> (int4)
  success for: '0' -> (i1)
  success for: '0' -> (i2)
  success for: '0' -> (i3)
  success for: '0' -> (i4)
  fail for: '0' -> i5 [23514: value for domain i5 violates check constraint "i5_check"]
  success for: '234' -> (int4)
  success for: '234' -> (i1)
  success for: '234' -> (i2)
  success for: '234' -> (i3)
  fail for: '234' -> i4 [23514: value for domain i4 violates check constraint "i4_check"]
  fail for: '234' -> i5 [23514: value for domain i5 violates check constraint "i4_check"]
  success for: 'None' -> (int4)
  success for: 'None' -> (i1)
  fail for: 'None' -> i2 [23502: domain i2 does not allow null values]
  fail for: 'None' -> i3 [23502: domain i3 does not allow null values]
  fail for: 'None' -> i4 [23502: domain i4 does not allow null values]
  fail for: 'None' -> i5 [23502: domain i5 does not allow null values]
 
 i1:
  success for: '-123' -> (int4)
  success for: '-123' -> (i1)
  success for: '-123' -> (i2)
  fail for: '-123' -> i3 [23514: value for domain i3 violates check constraint "i3_check"]
  fail for: '-123' -> i4 [23514: value for domain i4 violates check constraint "i3_check"]
  fail for: '-123' -> i5 [23514: value for domain i5 violates check constraint "i3_check"]
  success for: '0' -> (int4)
  success for: '0' -> (i1)
  success for: '0' -> (i2)
  success for: '0' -> (i3)
  success for: '0' -> (i4)
  fail for: '0' -> i5 [23514: value for domain i5 violates check constraint "i5_check"]
  success for: '234' -> (int4)
  success for: '234' -> (i1)
  success for: '234' -> (i2)
  success for: '234' -> (i3)
  fail for: '234' -> i4 [23514: value for domain i4 violates check constraint "i4_check"]
  fail for: '234' -> i5 [23514: value for domain i5 violates check constraint "i4_check"]
  success for: 'None' -> (int4)
  success for: 'None' -> (i1)
  fail for: 'None' -> i2 [23502: domain i2 does not allow null values]
  fail for: 'None' -> i3 [23502: domain i3 does not allow null values]
  fail for: 'None' -> i4 [23502: domain i4 does not allow null values]
  fail for: 'None' -> i5 [23502: domain i5 does not allow null values]
 
 i2:
  success for: '-123' -> (int4)
  success for: '-123' -> (i1)
  success for: '-123' -> (i2)
  fail for: '-123' -> i3 [23514: value for domain i3 violates check constraint "i3_check"]
  fail for: '-123' -> i4 [23514: value for domain i4 violates check constraint "i3_check"]
  fail for: '-123' -> i5 [23514: value for domain i5 violates check constraint "i3_check"]
  success for: '0' -> (int4)
  success for: '0' -> (i1)
  success for: '0' -> (i2)
  success for: '0' -> (i3)
  success for: '0' -> (i4)
  fail for: '0' -> i5 [23514: value for domain i5 violates check constraint "i5_check"]
  success for: '234' -> (int4)
  success for: '234' -> (i1)
  success for: '234' -> (i2)
  success for: '234' -> (i3)
  fail for: '234' -> i4 [23514: value for domain i4 violates check constraint "i4_check"]
  fail for: '234' -> i5 [23514: value for domain i5 violates check constraint "i4_check"]
 
 i3:
  success for: '0' -> (int4)
  success for: '0' -> (i1)
  success for: '0' -> (i2)
  success for: '0' -> (i3)
  success for: '0' -> (i4)
  fail for: '0' -> i5 [23514: value for domain i5 violates check constraint "i5_check"]
  success for: '123' -> (int4)
  success for: '123' -> (i1)
  success for: '123' -> (i2)
  success for: '123' -> (i3)
  fail for: '123' -> i4 [23514: value for domain i4 violates check constraint "i4_check"]
  fail for: '123' -> i5 [23514: value for domain i5 violates check constraint "i4_check"]
 
 i4:
  success for: '0' -> (int4)
  success for: '0' -> (i1)
  success for: '0' -> (i2)
  success for: '0' -> (i3)
  success for: '0' -> (i4)
  fail for: '0' -> i5 [23514: value for domain i5 violates check constraint "i5_check"]
 
 i5:
 
(1 row)

CREATE OR REPLACE FUNCTION
check_on_srf(OUT a i1, OUT i i2, OUT j i3, OUT j2 i4) RETURNS SETOF record
LANGUAGE python AS
$python$
def main():
	# last one is invalid
	return ((432, 432, 0, -1),)
$python$;
-- python is an untrusted language, the user must explicitly
-- check domain values.
SELECT check_on_srf();
  check_on_srf  
----------------
 (432,432,0,-1)
(1 row)

SELECT * FROM check_on_srf();
  a  |  i  | j | j2 
-----+-----+---+----
 432 | 432 | 0 | -1
(1 row)

