This file is indexed.

/usr/lib/python3/dist-packages/postgresql/types/io/stdlib_decimal.py is in python3-postgresql 1.1.0-1build1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
##
# types.io.stdlib_decimal
#
# I/O routines for transforming NUMERIC to and from decimal.Decimal.
##
from decimal import Decimal
from operator import itemgetter, mul
# You know it's gonna get serious :)
from itertools import chain, starmap, repeat, groupby, cycle, islice
from ...types import NUMERICOID
from . import lib

oid_to_type = {
	NUMERICOID: Decimal,
}

##
# numeric is represented using:
#  1. ndigits, the number of *numeric* digits.
#  2. weight, the *numeric* digits "left" of the decimal point
#  3. sign, negativity. see `numeric_signs` below
#  4. dscale, *display* precision. used to identify exponent.
#
# NOTE: A numeric digit is actually four digits in the representation.
#
# Python's Decimal consists of:
#  1. sign, negativity.
#  2. digits, sequence of int()'s
#  3. exponent, digits that fall to the right of the decimal point
numeric_negative = 16384

def numeric_pack(x,
	numeric_digit_length : "number of decimal digits in a numeric digit" = 4,
	get0 = itemgetter(0),
	get1 = itemgetter(1),
	Decimal = Decimal,
	pack = lib.numeric_pack
):
	if not isinstance(x, Decimal):
		x = Decimal(x)
	x = x.as_tuple()
	if x.exponent == 'F':
		raise ValueError("numeric does not support infinite values")

	# normalize trailing zeros (truncate em')
	# this is important in order to get the weight and padding correct
	# and to avoid packing superfluous data which will make pg angry.
	trailing_zeros = 0
	weight = 0
	if x.exponent < 0:
		# only attempt to truncate if there are digits after the point,
		##
		for i in range(-1, max(-len(x.digits), x.exponent)-1, -1):
			if x.digits[i] != 0:
				break
			trailing_zeros += 1
		# truncate trailing zeros right of the decimal point
		# this *is* the case as exponent < 0.
		if trailing_zeros:
			digits = x.digits[:-trailing_zeros]
		else:
			digits = x.digits
			# the entire exponent is just trailing zeros(zero-weight).
		rdigits = -(x.exponent + trailing_zeros)
		ldigits = len(digits) - rdigits
		rpad = rdigits % numeric_digit_length
		if rpad:
			rpad = numeric_digit_length - rpad
	else:
		# Need the weight to be divisible by four,
		# so append zeros onto digits until it is.
		r = (x.exponent % numeric_digit_length)
		if x.exponent and r:
			digits = x.digits + ((0,) * r)
			weight = (x.exponent - r)
		else:
			digits = x.digits
			weight = x.exponent
		# The exponent is not evenly divisible by four, so
		# the weight can't simple be x.exponent as it doesn't
		# match the size of the numeric digit.
		ldigits = len(digits)
		# no fractional quantity.
		rdigits = 0
		rpad = 0

	lpad = ldigits % numeric_digit_length
	if lpad:
		lpad = numeric_digit_length - lpad
	weight += (ldigits + lpad)

	digit_groups = map(
		get1,
		groupby(
			zip(
				# group by NUMERIC digit size,
				# every four digits make up a NUMERIC digit
				cycle((0,) * numeric_digit_length + (1,) * numeric_digit_length),

				# multiply each digit appropriately
				# for the eventual sum() into a NUMERIC digit
				starmap(
					mul,
					zip(
						# pad with leading zeros to make
						# the cardinality of the digit sequence
						# to be evenly divisible by four,
						# the NUMERIC digit size.
						chain(
							repeat(0, lpad),
							digits,
							repeat(0, rpad),
						),
						cycle([10**x for x in range(numeric_digit_length-1, -1, -1)]),
					)
				),
			),
			get0,
		),
	)
	return pack((
		(
			(ldigits + rdigits + lpad + rpad) // numeric_digit_length, # ndigits
			(weight // numeric_digit_length) - 1, # NUMERIC weight
			numeric_negative if x.sign == 1 else x.sign, # sign
			- x.exponent if x.exponent < 0 else 0, # dscale
		),
		list(map(sum, ([get1(y) for y in x] for x in digit_groups))),
	))

def numeric_convert_digits(d, str = str, int = int):
	i = iter(d)
	for x in str(next(i)):
		# no leading zeros
		yield int(x)
	# leading digit should not include zeros
	for y in i:
		for x in str(y).rjust(4, '0'):
			yield int(x)

numeric_signs = {
	numeric_negative : 1,
}

def numeric_unpack(x, unpack = lib.numeric_unpack):
	header, digits = unpack(x)
	npad = (header[3] - ((header[0] - (header[1] + 1)) * 4))
	return Decimal((
		numeric_signs.get(header[2], header[2]),
		tuple(chain(
			numeric_convert_digits(digits),
			(0,) * npad
		) if npad >= 0 else list(
			numeric_convert_digits(digits)
		)[:npad]),
		-header[3]
	))

oid_to_io = {
	NUMERICOID : (numeric_pack, numeric_unpack, Decimal),
}