Time to drop plpython2?

Started by Andres Freundabout 4 years ago86 messages
#1Andres Freund
andres@anarazel.de

Hi,

The last release of python2 was 2.7.18 released 2020-04-20, with support
already having ended before that 2020-01-01.

To me it seems time to drop plpython2 support. Supporting plpython2
until ~7 years after python2 is EOL is already quite long... It'd be one
thing if it were completely painless, but imo it's not.

I was about to list better plpython2/3 support (including the regex
replacements for the regress tests) as a TODO for the meson proposal,
but it doesn't really seem worth investing in that...

Greetings,

Andres Freund

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#1)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

To me it seems time to drop plpython2 support. Supporting plpython2
until ~7 years after python2 is EOL is already quite long... It'd be one
thing if it were completely painless, but imo it's not.

7 years? Oh, you're envisioning dropping plpython2 in HEAD but keeping
it alive in the back branches. Yeah, it's a bit hard to justify
continuing support in HEAD.

regards, tom lane

#3Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#2)
Re: Time to drop plpython2?

Hi,

On 2021-10-31 14:49:22 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

To me it seems time to drop plpython2 support. Supporting plpython2
until ~7 years after python2 is EOL is already quite long... It'd be one
thing if it were completely painless, but imo it's not.

7 years? Oh, you're envisioning dropping plpython2 in HEAD but keeping
it alive in the back branches.

Yea. It's annoying to support python2, but I don't think it's severe
enough to consider dropping support in released branches. We might get
to that point for the newer released versions, but for now...

Yeah, it's a bit hard to justify continuing support in HEAD.

Cool.

Greetings,

Andres Freund

#4Thomas Munro
thomas.munro@gmail.com
In reply to: Andres Freund (#3)
Re: Time to drop plpython2?

On Mon, Nov 1, 2021 at 8:05 AM Andres Freund <andres@anarazel.de> wrote:

Yeah, it's a bit hard to justify continuing support in HEAD.

+1, it's dropping out of distros, it'd be unsupportable without
unreasonable effort.

#5Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Andres Freund (#1)
Re: Time to drop plpython2?

On 31.10.21 19:45, Andres Freund wrote:

To me it seems time to drop plpython2 support. Supporting plpython2
until ~7 years after python2 is EOL is already quite long... It'd be one
thing if it were completely painless, but imo it's not.

I was about to list better plpython2/3 support (including the regex
replacements for the regress tests) as a TODO for the meson proposal,
but it doesn't really seem worth investing in that...

Usually, we have dropped support for older Python versions when
continued support would be a burden for some ongoing development
project. This would certainly count as one of those, I think.

#6Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Peter Eisentraut (#5)
Re: Time to drop plpython2?

I see you have posted a patch for this in the meson thread
(/messages/by-id/attachment/127770/v5-0003-plpython-Drop-support-python2.patch).
Here is my review of that.

I would change the search order in configure from

PGAC_PATH_PROGS(PYTHON, [python python3 python2])

to

PGAC_PATH_PROGS(PYTHON, [python3 python])

This makes sure you don't accidentally pick up a "python" binary that
points to Python 2. This would otherwise immediately generate lots of
build failures on older platforms that still have Python 2 around.

You left two FIXME sections in plpython.h. AFAICT, we can make the
substitutions corresponding to those #define's in the source code and
remove those blocks.

src/pl/plpython/expected/README should be updated for the removed files.

Some documentation updates are needed. I see possibly relevant text in
the following files:

hstore.sgml
install-windows.sgml
installation.sgml
json.sgml
libpq.sgml
plpython.sgml
ref/comment.sgml (examples)
ref/create_transform.sgml (examples)
ref/drop_transform.sgml (examples)
standalone-profile.xsl

src/tools/msvc/ has lots of Python-related content.

More stuff to clean up:

contrib/hstore_plpython/.gitignore
contrib/jsonb_plpython/.gitignore
contrib/ltree_plpython/.gitignore
src/pl/plpython/.gitignore

Finally, morally related, there is some Python 2/3 compat code in
contrib/unaccent/generate_unaccent_rules.py that could be removed.
Also, arguably, change the shebang line in that script.

#7Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#6)
Re: Time to drop plpython2?

Hi,

On 2021-11-04 19:58:54 +0100, Peter Eisentraut wrote:

I see you have posted a patch for this in the meson thread (/messages/by-id/attachment/127770/v5-0003-plpython-Drop-support-python2.patch).

Yea, I was planning to post that here after a bit more polish. I mostly wanted
to get rid of the gross gross hack I did for the transmutation of the
regression test files.

Here is my review of that.

I would change the search order in configure from

PGAC_PATH_PROGS(PYTHON, [python python3 python2])

to

PGAC_PATH_PROGS(PYTHON, [python3 python])

This makes sure you don't accidentally pick up a "python" binary that points
to Python 2. This would otherwise immediately generate lots of build
failures on older platforms that still have Python 2 around.

Yes, we should do that, at least for now. I did see build failures that
required me to specify the python version to avoid issues around it.

You left two FIXME sections in plpython.h. AFAICT, we can make the
substitutions corresponding to those #define's in the source code and remove
those blocks.

Yea, it shouldn't be hard. Just required more time than I had to send it out
before Nov 1st ;)

With meson I'd do a version: '>= 3' or such, to filter out a bare 'python'
being python2, but I don't think there's an equally trivial way to do that
with autoconf.

Finally, morally related, there is some Python 2/3 compat code in
contrib/unaccent/generate_unaccent_rules.py that could be removed. Also,
arguably, change the shebang line in that script.

Hm. So far the python used for plpython and python for code generation etc is
independent. I don't know if plpython actually can be cross-compiled, but if
so, they'd have to be independent. Otherwise I'd say we should just invoke
contrib/unaccent/generate_unaccent_rules.py with a python chosen by
configure/meson, rather than relying on a shebang that can't be adjusted
without modifying source code.

Another thing I wondered about is what we want to do with the extension
names. Do we want to leave it named plpython3u? Do we want to have a plpython
that depends on plpython3u?

I'd be inclined to just keep it at plpython3u for now, but there's an argument
that going for plpython would be better long term: Presumably there will be
python 4 at some point - but I'd expect that to not be a breaking release,
given the disaster that python 3 is. Making a non-versioned name better?

Greetings,

Andres Freund

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#7)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

Another thing I wondered about is what we want to do with the extension
names. Do we want to leave it named plpython3u? Do we want to have a plpython
that depends on plpython3u?

I think we want to keep plpython3u. Maybe we can point plpythonu
at that, but I'm concerned about the potential for user confusion.
In particular, I think there's a nonzero probability that someone
will choose to maintain plpython2u/plpythonu outside of core,
just because they still don't want to migrate their Python code.

I'd be inclined to just keep it at plpython3u for now, but there's an argument
that going for plpython would be better long term: Presumably there will be
python 4 at some point - but I'd expect that to not be a breaking release,
given the disaster that python 3 is. Making a non-versioned name better?

Meh. If there is a python 4, I'd expect it to be named that way precisely
because it *is* a breaking release. Why would we set ourselves up for
a repeat of this mess?

regards, tom lane

#9Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Andres Freund (#7)
Re: Time to drop plpython2?

On 04.11.21 20:54, Andres Freund wrote:

Finally, morally related, there is some Python 2/3 compat code in
contrib/unaccent/generate_unaccent_rules.py that could be removed. Also,
arguably, change the shebang line in that script.

Hm. So far the python used for plpython and python for code generation etc is
independent. I don't know if plpython actually can be cross-compiled, but if
so, they'd have to be independent. Otherwise I'd say we should just invoke
contrib/unaccent/generate_unaccent_rules.py with a python chosen by
configure/meson, rather than relying on a shebang that can't be adjusted
without modifying source code.

We don't rely on the shebang for running this (see Makefile). I just
see some trend of people changing shebang lines as a sort of signal,
"this script has abandoned Python 2". It's not very important.

Another thing I wondered about is what we want to do with the extension
names. Do we want to leave it named plpython3u? Do we want to have a plpython
that depends on plpython3u?

I would tend to mirror what the Python community does with
/usr/bin/python. Right now, most people are removing /usr/bin/python
and just provide /usr/bin/python3. This would be analogous to what your
patch does. Some time in the future, they may add /usr/bin/python back,
and then we could do the same, meaning add a plpythonu that depends on
plpython3u.

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#9)
Re: Time to drop plpython2?

... btw, there's a fairly critical gating factor for any plan to drop
python2 support: the buildfarm. I just counted, and there are exactly
as many members running python 2.x as 3.x (49 apiece), not counting
Windows machines that aren't running configure. We can't commit
something that's going to make half the buildfarm go red.

(It's likely that some fraction of them do already have python3 installed,
in which case the search order change Peter recommended would be enough to
fix it. But I'm sure not all do.)

This ties into the business about converting the build system to meson,
as that also requires python 3 --- with, IIUC, a higher minimum version
than we might otherwise need. I'm disinclined to cause two separate
flag days for buildfarm owners, so what I now think is we ought to put
this idea on the shelf until we've finished that conversion or decided
we're not gonna do it. We need to identify exactly what needs to be
installed before we start pestering the owners.

regards, tom lane

#11Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#10)
Re: Time to drop plpython2?

Hi,

On 2021-11-14 21:24:31 -0500, Tom Lane wrote:

... btw, there's a fairly critical gating factor for any plan to drop
python2 support: the buildfarm. I just counted, and there are exactly
as many members running python 2.x as 3.x (49 apiece), not counting
Windows machines that aren't running configure. We can't commit
something that's going to make half the buildfarm go red.

(It's likely that some fraction of them do already have python3 installed,
in which case the search order change Peter recommended would be enough to
fix it. But I'm sure not all do.)

How about committing the order change alone? That seems like something
warranted completely in isolation? Afterwards we can see how many run what and
go from there?

This ties into the business about converting the build system to meson,
as that also requires python 3 --- with, IIUC, a higher minimum version
than we might otherwise need. I'm disinclined to cause two separate
flag days for buildfarm owners, so what I now think is we ought to put
this idea on the shelf until we've finished that conversion or decided
we're not gonna do it. We need to identify exactly what needs to be
installed before we start pestering the owners.

Yea, that's true.

Greetings,

Andres Freund

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#11)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

On 2021-11-14 21:24:31 -0500, Tom Lane wrote:

(It's likely that some fraction of them do already have python3 installed,
in which case the search order change Peter recommended would be enough to
fix it. But I'm sure not all do.)

How about committing the order change alone? That seems like something
warranted completely in isolation? Afterwards we can see how many run what and
go from there?

I don't think that's warranted. The existing design is that we let
the user say which python is "python", and I do not think we should
change that in advance of actually dropping python2 support.

I was wondering about simply probing to see if python3 exists (and if
so, what version it is exactly), as an additional configure test that
doesn't hook into anything. That would give us some information without
suddenly changing what is being tested.

regards, tom lane

#13Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#12)
Re: Time to drop plpython2?

Hi,

On 2021-11-15 12:19:51 -0500, Tom Lane wrote:

I don't think that's warranted. The existing design is that we let
the user say which python is "python", and I do not think we should
change that in advance of actually dropping python2 support.

Hm. I think it'd be ok, given that python 2 is well past EOL. But I also see
your point.

I was wondering about simply probing to see if python3 exists (and if
so, what version it is exactly), as an additional configure test that
doesn't hook into anything. That would give us some information without
suddenly changing what is being tested.

But this is probably a good compromise. Were you thinking of doing a proper
autoconf test or just putting something like python3 --version || true in
configure?

I guess it'd be easiest to interpret if we output the current PYTHON version
and, iff < 3.0, also output the 'python3' version?

Greetings,

Andres Freund

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#13)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

On 2021-11-15 12:19:51 -0500, Tom Lane wrote:

I was wondering about simply probing to see if python3 exists (and if
so, what version it is exactly), as an additional configure test that
doesn't hook into anything. That would give us some information without
suddenly changing what is being tested.

But this is probably a good compromise. Were you thinking of doing a proper
autoconf test or just putting something like python3 --version || true in
configure?

Hm, I had in mind an actual configure test; but since it's just a
temporary exploratory measure, shortcuts are fine. However, I'm
not sure that what you suggest would result in capturing anything
in config.log.

I guess it'd be easiest to interpret if we output the current PYTHON version
and, iff < 3.0, also output the 'python3' version?

We'll already know if PYTHON is set, at least so far as the buildfarm
is concerned.

regards, tom lane

#15Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#14)
Re: Time to drop plpython2?

On 15.11.21 19:18, Tom Lane wrote:

Andres Freund<andres@anarazel.de> writes:

On 2021-11-15 12:19:51 -0500, Tom Lane wrote:

I was wondering about simply probing to see if python3 exists (and if
so, what version it is exactly), as an additional configure test that
doesn't hook into anything. That would give us some information without
suddenly changing what is being tested.

But this is probably a good compromise. Were you thinking of doing a proper
autoconf test or just putting something like python3 --version || true in
configure?

Hm, I had in mind an actual configure test; but since it's just a
temporary exploratory measure, shortcuts are fine. However, I'm
not sure that what you suggest would result in capturing anything
in config.log.

I'm not sure this is really going to end up moving things forward.

I think we should just write to the build farm owners, we plan to drop
python2 support in, say, 60 days, please update your setup to use
python3 or disable python support.

If we add this test first, then all we're going to learn is probably
that 60% of those who are currently using python2 don't have python3
installed, and then we're still going to have to send that above email.

#16Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#15)
Re: Time to drop plpython2?

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

If we add this test first, then all we're going to learn is probably
that 60% of those who are currently using python2 don't have python3
installed, and then we're still going to have to send that above email.

I don't know what fraction don't have python3 installed, and it doesn't
matter much, since it's unlikely that it's either 0% or 100%. What
I hoped to learn was, of those who *do* have some python3 installed,
which version it is. That might inform our thoughts about where to
set the minimum python3 version.

Relevant data points:

* Our docs claim the minimum 3.x version for pl/python itself is 3.1,
but this is unbacked by any testing; the oldest 3.x in the buildfarm
is 3.4.3 (three such animals).

* Meson only promises support back to python 3.6, but if that's
accurate it's going to be a problem for us, because there are lots
of live LTS distributions with older python3 (RHEL7, Solaris 11.3,
AIX 7.2 for starters). I've been planning to do some testing and
see if meson will run under python 3.4 or 3.5.

But this probably belongs on the meson thread, since that looks like
it will be a much stronger constraint than pl/python is.

regards, tom lane

#17Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#16)
Re: Time to drop plpython2?

Hi,

Continuing the discussion from /messages/by-id/2146739.1637004415@sss.pgh.pa.us

On 2021-11-15 14:26:55 -0500, Tom Lane wrote:

* Meson only promises support back to python 3.6, but if that's
accurate it's going to be a problem for us, because there are lots
of live LTS distributions with older python3 (RHEL7, Solaris 11.3,
AIX 7.2 for starters). I've been planning to do some testing and
see if meson will run under python 3.4 or 3.5.

Slightly older versions, which do work to build postgres with the proposed
patchset, run on python3 3.5. I don't think it's likely we could make the
versions that only required 3.4 work reasonably.

Is RHEL7 really an issue? I only have Centos 7 around, but that has python
3.6.

I don't know much about AIX, but according to https://repology.org/project/python/versions
the AIX toolbox has 3.7.11. I don't know enough about AIX to know whether
there's other sources of python3, bison, etc that are common. On the AIX
system I have access to they all seem to be symlinked to /opt/freeware, which
I understand is that toolbox stuff?

Greetings,

Andres Freund

#18Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#17)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

On 2021-11-15 14:26:55 -0500, Tom Lane wrote:

* Meson only promises support back to python 3.6, but if that's
accurate it's going to be a problem for us, because there are lots
of live LTS distributions with older python3 (RHEL7, Solaris 11.3,
AIX 7.2 for starters). I've been planning to do some testing and
see if meson will run under python 3.4 or 3.5.

Slightly older versions, which do work to build postgres with the proposed
patchset, run on python3 3.5. I don't think it's likely we could make the
versions that only required 3.4 work reasonably.

OK, thanks for the datapoint.

Is RHEL7 really an issue? I only have Centos 7 around, but that has python
3.6.

The info I checked said that RHEL7 originally shipped with 3.3.
I'm not sure that Red Hat would've outright replaced that, but they
do have a notion of add-on "software collections", and I'm certain
that they would have provided newer pythons via that mechanism.
So it should only be a big issue for someone who didn't want to install
an add-on collection. I suppose the overlap of that group with the
people who will want to put PG 15+ on that platform is probably nil.

I don't know much about AIX, but according to https://repology.org/project/python/versions
the AIX toolbox has 3.7.11. I don't know enough about AIX to know whether
there's other sources of python3, bison, etc that are common. On the AIX
system I have access to they all seem to be symlinked to /opt/freeware, which
I understand is that toolbox stuff?

Hmm, I was basing that on (a) what I can see installed on gcc119,
which is 3.5.1, and (b) AIX 7.2's 2015 release date, which matches up
pretty well with python 3.5. As with RHEL, it's entirely likely that
IBM has made newer releases available as add-ons, but there's no
guarantee that any given installation would have that.

Solaris is a bit worse, since they shipped 11.3 a shade earlier,
with python 3.4.3. No idea about add-on conventions on that
platform.

There's room to argue that all three of these will be out of
primary support before PG15 ships, so maybe we don't need to
worry about whether we can build with their default toolsets.
Still, it's a tradeoff I'd rather not make.

regards, tom lane

#19Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#18)
Re: Time to drop plpython2?

On Mon, Nov 15, 2021 at 3:30 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

The info I checked said that RHEL7 originally shipped with 3.3.
I'm not sure that Red Hat would've outright replaced that, but they
do have a notion of add-on "software collections", and I'm certain
that they would have provided newer pythons via that mechanism.
So it should only be a big issue for someone who didn't want to install
an add-on collection. I suppose the overlap of that group with the
people who will want to put PG 15+ on that platform is probably nil.

It'd only be an issue if they want to compile from source, right?
We're not speaking of changing the runtime prerequisites, IIUC.

I think it's really important that we continue to run on all of the
supported Linux distributions and even some recently-out-of-support
ones if they are popular. My experience is that many people stay on
whatever version of RHEL they've got for quite a long time. I think
we'll be doing ourselves a disservice if such people find that they
can't easily upgrade to newer versions of PostgreSQL without
installing a bunch of new software and/or upgrading software they've
already got on the machine.

On the other hand, the class of users that I'm thinking about does not
typically install anything from source, so they are not directly
impacted by build prerequisites.

--
Robert Haas
EDB: http://www.enterprisedb.com

#20Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#18)
Re: Time to drop plpython2?

Hi,

On 2021-11-15 15:30:02 -0500, Tom Lane wrote:

Is RHEL7 really an issue? I only have Centos 7 around, but that has python
3.6.

The info I checked said that RHEL7 originally shipped with 3.3.
I'm not sure that Red Hat would've outright replaced that, but they
do have a notion of add-on "software collections", and I'm certain
that they would have provided newer pythons via that mechanism.
So it should only be a big issue for someone who didn't want to install
an add-on collection. I suppose the overlap of that group with the
people who will want to put PG 15+ on that platform is probably nil.

3.6 appears to be in the 'update' repository, which I think is separate from
the add-on collection? It appears to be enabled by default in centos7, not
sure how much that means.

Name : python3
Version : 3.6.8
Release : 18.el7
Architecture: x86_64
Size : 39896
Packager : CentOS BuildSystem <http://bugs.centos.org&gt;
Group : Unspecified
URL : https://www.python.org/
Repository : updates
Summary : Interpreter of the Python programming language
Source : python3-3.6.8-18.el7.src.rpm

Hmm, I was basing that on (a) what I can see installed on gcc119,
which is 3.5.1, and (b) AIX 7.2's 2015 release date, which matches up
pretty well with python 3.5. As with RHEL, it's entirely likely that
IBM has made newer releases available as add-ons, but there's no
guarantee that any given installation would have that.

I think AIX itself only ships with python 2.7, and all python3 versions are
from toolbox. But I don't actually know.

Solaris is a bit worse, since they shipped 11.3 a shade earlier,
with python 3.4.3. No idea about add-on conventions on that
platform.

Based solely on some person in meson's irc channel, it appears that they added
a newer python3 sometime in 11.4's lifetime:

alanc:
Solaris 11.4 currently ships Python 3.7 & 3.9
as the maintainer of the meson package for Solaris, I deal with the
lack of newer python versions on older Solaris releases by only packaging
meson for Solaris 11.4, and telling anyone who wants it for older releases to
upgrade their OS, or build all the dependencies themselves
...
or it's 11.4.0 from 2018, and not updated with the latest updates (which are
only available for support customers right now unfortunately) - 3.7 was added
in 11.4.12
https://docs.oracle.com/cd/E37838_01/html/E99082/bundledfreeware.html
...

There's room to argue that all three of these will be out of
primary support before PG15 ships, so maybe we don't need to
worry about whether we can build with their default toolsets.
Still, it's a tradeoff I'd rather not make.

There's two other paths worth mentioning:
- muon is a WIP alternative implementation of meson in plain C99. Doesn't yet
have enough feature coverage.
- pyinstaller generates an executable from the meson python code, and supports
solaris and aix

Greetings,

Andres Freund

#21Andres Freund
andres@anarazel.de
In reply to: Robert Haas (#19)
Re: Time to drop plpython2?

Hi,

On November 15, 2021 12:36:11 PM PST, Robert Haas <robertmhaas@gmail.com> wrote:

It'd only be an issue if they want to compile from source, right?
We're not speaking of changing the runtime prerequisites, IIUC.

Correct.
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

#22Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#19)
Re: Time to drop plpython2?

Robert Haas <robertmhaas@gmail.com> writes:

On Mon, Nov 15, 2021 at 3:30 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

The info I checked said that RHEL7 originally shipped with 3.3.

It'd only be an issue if they want to compile from source, right?
We're not speaking of changing the runtime prerequisites, IIUC.

I'm not sure. Does it make sense to document that pl/python has
a different Python version requirement than the build system does?
If we do, who exactly is going to be testing that such a combination
works? Will it even be possible to compile pl/python against Python
headers/libs of a different Python generation than meson is running
under?

ISTM we'd be a lot better off saying "the minimum Python version is
3.something", full stop, and then making sure that that minimum is
represented in the buildfarm. But it's not quite clear yet what
"something" needs to be.

I think it's really important that we continue to run on all of the
supported Linux distributions and even some recently-out-of-support
ones if they are popular.

I agree completely, which is why I'm raising the point.

regards, tom lane

#23Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#16)
Re: Time to drop plpython2?

On 15.11.21 20:26, Tom Lane wrote:

* Our docs claim the minimum 3.x version for pl/python itself is 3.1,
but this is unbacked by any testing; the oldest 3.x in the buildfarm
is 3.4.3 (three such animals).

I confirmed locally that 3.2.6 still works with PL/Python. I expect
that Python 3.1 also still works, except that I can't build it at the
moment. Python 3.0.* was sort of a dud, so it's not really interesting
to consider.

Partially because of the need to keep supporting Python 2, there hasn't
been any work done in PL/Python to make use of any newer APIs, so the
Python 3 minimum version is frozen in time at the moment.

#24Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#22)
Re: Time to drop plpython2?

On Mon, Nov 15, 2021 at 4:12 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

It'd only be an issue if they want to compile from source, right?
We're not speaking of changing the runtime prerequisites, IIUC.

I'm not sure. Does it make sense to document that pl/python has
a different Python version requirement than the build system does?
If we do, who exactly is going to be testing that such a combination
works? Will it even be possible to compile pl/python against Python
headers/libs of a different Python generation than meson is running
under?

Hmm, that's true. I hadn't considered the fact that anyone who is
packaging PostgreSQL probably also wants to build plpython. However,
it's possible that a side-by-side install of a newer python version
could be used for the build system while building against the system
python for plpython. That might or might not be too exotic a
configuration for someone to consider.

--
Robert Haas
EDB: http://www.enterprisedb.com

#25Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#10)
Re: Time to drop plpython2?

On 11/14/21 21:24, Tom Lane wrote:

... btw, there's a fairly critical gating factor for any plan to drop
python2 support: the buildfarm. I just counted, and there are exactly
as many members running python 2.x as 3.x (49 apiece), not counting
Windows machines that aren't running configure. We can't commit
something that's going to make half the buildfarm go red.

(It's likely that some fraction of them do already have python3 installed,
in which case the search order change Peter recommended would be enough to
fix it. But I'm sure not all do.)

This ties into the business about converting the build system to meson,
as that also requires python 3 --- with, IIUC, a higher minimum version
than we might otherwise need. I'm disinclined to cause two separate
flag days for buildfarm owners, so what I now think is we ought to put
this idea on the shelf until we've finished that conversion or decided
we're not gonna do it. We need to identify exactly what needs to be
installed before we start pestering the owners.

crake has been using 2.7, but has 3.9.7 installed. I tried switching to
that but ran into an issue with upgrading things from 9.5 on. It would
have been all the way back to 9.2 but the plpython tests drop the
extension even though the transform tests in contrib don't, and neither
do the plperl tests drop plperlu. I'm rather inclined to say we should
stop doing that, or at least be consistent about it.

Having rebuilt all the branches from 9.5 and up the cross version
upgrade tests are now passing on crake.

My other machine with an old python instance is bowerbird. It has python
3.4 installed but not used, alongside 2.7 which is udsed. I will install
the latest and see if that can be made to work.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com

#26Andrew Dunstan
andrew@dunslane.net
In reply to: Andrew Dunstan (#25)
Re: Time to drop plpython2?

On 11/16/21 11:26, Andrew Dunstan wrote:

My other machine with an old python instance is bowerbird. It has python
3.4 installed but not used, alongside 2.7 which is udsed. I will install
the latest and see if that can be made to work.

bowerbird is now building with python 3.10

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com

#27Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Peter Eisentraut (#15)
Re: Time to drop plpython2?

On 15.11.21 19:52, Peter Eisentraut wrote:

I think we should just write to the build farm owners, we plan to drop
python2 support in, say, 60 days, please update your setup to use
python3 or disable python support.

This discussion stalled. I think we should do *something* for
PostgreSQL 15.

I suspect at this point, the Meson move isn't going to happen for
PostgreSQL 15. Even if the code were ready, which it is not, then this
kind of thing would surely be something more sensible to put into PG16
early rather than PG15 late. So I'm setting aside any concerns about
which Python 3.x is the appropriate minimum level.

There is the discussion [0]/messages/by-id/c74add3c-09c4-a9dd-1a03-a846e5b2fc52@enterprisedb.com, which involves raising the minimum Python
version from 2.6 to 2.7, which would affect some build farm members.
But if we're going to drop 2.x anyway for PG15, say, then we don't need
to spend time organizing a 2.6 to 2.7 transition. (Although
backpatching might be required.)

Also, I was recently doing some bug fixing work in PL/Python, and just
getting a Python 2 installation is not straightforward these days on the
sort of OS that might be on a development machine. So there is also
that driver for dropping Python 2.

I think what I wrote in the message I'm responding to is the best way
forward, although maybe with less than 60 days now.

Thoughts?

[0]: /messages/by-id/c74add3c-09c4-a9dd-1a03-a846e5b2fc52@enterprisedb.com
/messages/by-id/c74add3c-09c4-a9dd-1a03-a846e5b2fc52@enterprisedb.com

#28Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#27)
Re: Time to drop plpython2?

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 15.11.21 19:52, Peter Eisentraut wrote:

I think we should just write to the build farm owners, we plan to drop
python2 support in, say, 60 days, please update your setup to use
python3 or disable python support.

This discussion stalled. I think we should do *something* for
PostgreSQL 15.

Agreed. We want plpython2 out of there for v15, or we'll be adding
another year to the length of time we're on the hook to support
python2 in the back branches --- which, as you say, is getting
ever more painful to do.

I suspect at this point, the Meson move isn't going to happen for
PostgreSQL 15.

Also agreed. Nonetheless, we need to make a recommendation to the
buildfarm owners about what's the minimum python3 version we intend
to support going forward. Do we want to just set it at 3.6, with
the expectation that the meson move will happen before too long?

(I'm not going to accept being fuzzy on this point, because I need
to know what to install on prairiedog and gaur. I intend to install
whatever our minimum supported version is, not only to keep us honest
on that being actually supported, but because I anticipate that
newer python versions will be correspondingly harder to build on
ancient platforms.)

regards, tom lane

#29Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#28)
Re: Time to drop plpython2?

On 11.01.22 17:06, Tom Lane wrote:

Nonetheless, we need to make a recommendation to the
buildfarm owners about what's the minimum python3 version we intend
to support going forward. Do we want to just set it at 3.6, with
the expectation that the meson move will happen before too long?

Well, the minimum supported version has always been the oldest version
that actually works. I don't think we ever said, we support >= X, even
though < X still actually works, about any dependency.

I don't care much to tie this to Meson right now. Meson might well move
to 3.8 next week and ruin this whole scheme.

I'm okay with issuing some sort of recommendation for what is reasonable
to test, and 3.5 or 3.6 seems like a good cutoff, considering what LTS
OS currently ship. But I'm not sure if that is the same as "support".

#30Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#29)
Re: Time to drop plpython2?

On Wed, Jan 12, 2022 at 2:39 AM Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:

Well, the minimum supported version has always been the oldest version
that actually works. I don't think we ever said, we support >= X, even
though < X still actually works, about any dependency.

I think that we sometimes say that versions < X are unsupported if we
are unable to test whether or not they work. In other words, I think
the relevant question is whether we are able to demonstrate that it
works, not whether it actually does work.

--
Robert Haas
EDB: http://www.enterprisedb.com

#31Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#29)
Re: Time to drop plpython2?

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 11.01.22 17:06, Tom Lane wrote:

Nonetheless, we need to make a recommendation to the
buildfarm owners about what's the minimum python3 version we intend
to support going forward.

Well, the minimum supported version has always been the oldest version
that actually works. I don't think we ever said, we support >= X, even
though < X still actually works, about any dependency.

The concern I have is how do we know what "actually works", if we're
not testing it? installation.sgml currently promises python2 >= 2.6,
and we know that that works because we have 2.6 in the buildfarm.
It also promises python3 >= 3.1, but we have no buildfarm animals
testing anything older than 3.4.3, so I don't think that promise
is worth the electrons it's written on. Furthermore, if the meson
conversion forces people to update their python3 to something newer,
there will probably be no testing of plpython against anything older
than what meson requires.

I don't care much to tie this to Meson right now. Meson might well move
to 3.8 next week and ruin this whole scheme.

Wouldn't be a problem unless our build scripts require that newer
version of meson. Andres mentioned earlier that we should be able
to run with some older meson versions that only require python 3.5
or so, so I'm hoping we can end up with directives like "use meson
X or later and python 3.5 or later".

I'm okay with issuing some sort of recommendation for what is reasonable
to test, and 3.5 or 3.6 seems like a good cutoff, considering what LTS
OS currently ship. But I'm not sure if that is the same as "support".

Well, I'll see about putting 3.5 on my dinosaurs, and hope I don't
have to do it over.

Anyway, getting back to the point: I think we should notify the
owners ASAP and set a 30-day deadline. We should try to get this
done before the March CF starts, so it's too late for a 60-day
grace period. In any case, the worst-case scenario for an owner
is to disable --with-python until they have time to do an upgrade,
so it doesn't seem like a month is a big problem.

regards, tom lane

#32Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#31)
Re: Time to drop plpython2?

On 12.01.22 19:49, Tom Lane wrote:

Anyway, getting back to the point: I think we should notify the
owners ASAP and set a 30-day deadline. We should try to get this
done before the March CF starts, so it's too late for a 60-day
grace period. In any case, the worst-case scenario for an owner
is to disable --with-python until they have time to do an upgrade,
so it doesn't seem like a month is a big problem.

Sure, let's do that. I don't have a buildfarm animal these days, so I'm
not on that list, so it would be great if you could do that

#33Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#32)
Re: Time to drop plpython2?

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 12.01.22 19:49, Tom Lane wrote:

Anyway, getting back to the point: I think we should notify the
owners ASAP and set a 30-day deadline.

Sure, let's do that. I don't have a buildfarm animal these days, so I'm
not on that list, so it would be great if you could do that

Done. I told them "mid February", so we can plan on say the 15th
as the target date for pushing a patch.

I realized BTW that the meson business is not relevant for prairiedog
or gaur. Those animals will die regardless of python version because
they can't build ninja (for lack of <spawn.h>). So I think maybe
I'll install python 3.1 and see if that compatibility claim is really
true ;-)

regards, tom lane

#34Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#27)
Re: Time to drop plpython2?

Hi,

On 2022-01-11 13:59:32 +0100, Peter Eisentraut wrote:

I suspect at this point, the Meson move isn't going to happen for PostgreSQL
15.

Yes - I IIRC even noted early in the thread that I don't think it's realistic
to finish it in time for 15. There's just a good amount of portability hacking
left to be done, and a good chunk of cleanup. I have a new colleague working
on automating testing it on more platforms.

Getting a few of the prerequisite patches in would make it more likely to
succeed in 16 though.

Even if the code were ready, which it is not, then this kind of thing
would surely be something more sensible to put into PG16 early rather than
PG15 late.

Right. There's also a lot of discussion around transition paths etc that need
to be happening first. A lot of other projects that moved ran with multiple
buildsystems for ~1 release, so things could mopped up without blocking
everyone.

I think what I wrote in the message I'm responding to is the best way
forward, although maybe with less than 60 days now.

Already happened, but I'm a belated +1 ;)

Greetings,

Andres Freund

#35Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#33)
Re: Time to drop plpython2?

I wrote:

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 12.01.22 19:49, Tom Lane wrote:

Anyway, getting back to the point: I think we should notify the
owners ASAP and set a 30-day deadline.

Sure, let's do that. I don't have a buildfarm animal these days, so I'm
not on that list, so it would be great if you could do that

Done. I told them "mid February", so we can plan on say the 15th
as the target date for pushing a patch.

Well, it's mid-February. Do we have a python2-removal patch
that's ready to go?

regards, tom lane

#36Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#35)
Re: Time to drop plpython2?

Hi,

On 2022-02-14 14:18:58 -0500, Tom Lane wrote:

Well, it's mid-February. Do we have a python2-removal patch
that's ready to go?

I can refresh mine. Iit might be good to first reapply
f201da39edc - "Make configure prefer python3 to plain python."
for a few days?

Greetings,

Andres Freund

#37Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#36)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

On 2022-02-14 14:18:58 -0500, Tom Lane wrote:

Well, it's mid-February. Do we have a python2-removal patch
that's ready to go?

I can refresh mine. Iit might be good to first reapply
f201da39edc - "Make configure prefer python3 to plain python."
for a few days?

We could I guess, but does it really buy anything? I'm sure that
some of the buildfarm still hasn't updated their Python installation,
but it'll be about the same failure we'd get from the final patch.

regards, tom lane

#38Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#37)
Re: Time to drop plpython2?

Hi,

On February 14, 2022 12:48:12 PM PST, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Andres Freund <andres@anarazel.de> writes:

On 2022-02-14 14:18:58 -0500, Tom Lane wrote:

Well, it's mid-February. Do we have a python2-removal patch
that's ready to go?

I can refresh mine. Iit might be good to first reapply
f201da39edc - "Make configure prefer python3 to plain python."
for a few days?

We could I guess, but does it really buy anything? I'm sure that
some of the buildfarm still hasn't updated their Python installation,
but it'll be about the same failure we'd get from the final patch.

I guess what I was actually wondering about - but didn't write - was whether it's worth rejecting python 2 with just configure / msvc perl changes initially.

The proper removal of python 2 support includes a bunch of buildsystem and code changes. Seems like it could be useful to have a snapshot of the buildfarm state after rejecting python 2, separate from the more verbose changes.

Andres

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

#39Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#38)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

On February 14, 2022 12:48:12 PM PST, Tom Lane <tgl@sss.pgh.pa.us> wrote:

We could I guess, but does it really buy anything? I'm sure that
some of the buildfarm still hasn't updated their Python installation,
but it'll be about the same failure we'd get from the final patch.

I guess what I was actually wondering about - but didn't write - was whether it's worth rejecting python 2 with just configure / msvc perl changes initially.

Ah, I see. +1, that would let us start nagging the laggard buildfarm
owners right away. But I do think we want the rest of the cleanup
done pretty soon --- especially simplification of the plpython
regression test arrangements.

regards, tom lane

#40Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#39)
1 attachment(s)
Re: Time to drop plpython2?

Hi,

On 2022-02-14 22:57:37 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On February 14, 2022 12:48:12 PM PST, Tom Lane <tgl@sss.pgh.pa.us> wrote:

We could I guess, but does it really buy anything? I'm sure that
some of the buildfarm still hasn't updated their Python installation,
but it'll be about the same failure we'd get from the final patch.

I guess what I was actually wondering about - but didn't write - was whether it's worth rejecting python 2 with just configure / msvc perl changes initially.

Ah, I see. +1, that would let us start nagging the laggard buildfarm
owners right away.

Cool.

But I do think we want the rest of the cleanup done pretty soon ---
especially simplification of the plpython regression test arrangements.

That part I have mostly ready (worked as part of a rebase of the meson tree
sometime last weekend). It's trawling through the docs, the msvc build scripts
and some plpython C code that's a bit more work...

Attached the state as I have in the meson tree. Will split out the configure
test tomorrow. Might or might get through the msvc scripts and docs as well.

Greetings,

Andres Freund

Attachments:

v3-0001-plpython-Drop-support-for-python-2.patchtext/x-diff; charset=us-asciiDownload
From 07eea3f46b05cb32120f31a1037d20e9928b0b95 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Wed, 2 Feb 2022 16:41:51 -0800
Subject: [PATCH v3] plpython: Drop support for python 2.

Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 config/python.m4                              |    4 +-
 src/pl/plpython/Makefile                      |   14 -
 src/pl/plpython/expected/plpython_call.out    |   12 +-
 .../plpython/expected/plpython_composite.out  |   32 +-
 src/pl/plpython/expected/plpython_do.out      |    8 +-
 src/pl/plpython/expected/plpython_drop.out    |    3 +-
 src/pl/plpython/expected/plpython_ereport.out |   22 +-
 src/pl/plpython/expected/plpython_error.out   |   52 +-
 src/pl/plpython/expected/plpython_error_5.out |  447 --------
 src/pl/plpython/expected/plpython_global.out  |    6 +-
 src/pl/plpython/expected/plpython_import.out  |    8 +-
 src/pl/plpython/expected/plpython_newline.out |    6 +-
 src/pl/plpython/expected/plpython_params.out  |    8 +-
 src/pl/plpython/expected/plpython_quote.out   |    2 +-
 src/pl/plpython/expected/plpython_record.out  |   18 +-
 src/pl/plpython/expected/plpython_setof.out   |   18 +-
 src/pl/plpython/expected/plpython_spi.out     |   48 +-
 .../expected/plpython_subtransaction.out      |   38 +-
 src/pl/plpython/expected/plpython_test.out    |   12 +-
 .../expected/plpython_transaction.out         |   24 +-
 src/pl/plpython/expected/plpython_trigger.out |   46 +-
 src/pl/plpython/expected/plpython_types.out   |  230 ++--
 src/pl/plpython/expected/plpython_types_3.out | 1009 -----------------
 src/pl/plpython/expected/plpython_unicode.out |   16 +-
 src/pl/plpython/expected/plpython_void.out    |    6 +-
 src/pl/plpython/plpy_cursorobject.c           |    2 +-
 src/pl/plpython/plpy_main.c                   |   55 +-
 src/pl/plpython/plpy_plpymodule.c             |   16 -
 src/pl/plpython/plpy_plpymodule.h             |    2 -
 src/pl/plpython/plpy_resultobject.c           |    8 -
 src/pl/plpython/plpy_typeio.c                 |    8 -
 src/pl/plpython/plpy_util.c                   |    3 -
 src/pl/plpython/plpy_util.h                   |    2 -
 src/pl/plpython/plpython.h                    |   13 +-
 src/pl/plpython/plpython2u--1.0.sql           |   17 -
 src/pl/plpython/plpython2u.control            |    7 -
 src/pl/plpython/plpythonu--1.0.sql            |   17 -
 src/pl/plpython/plpythonu.control             |    7 -
 src/pl/plpython/regress-python3-mangle.mk     |   38 -
 src/pl/plpython/sql/plpython_call.sql         |   12 +-
 src/pl/plpython/sql/plpython_composite.sql    |   32 +-
 src/pl/plpython/sql/plpython_do.sql           |    6 +-
 src/pl/plpython/sql/plpython_drop.sql         |    4 +-
 src/pl/plpython/sql/plpython_ereport.sql      |   22 +-
 src/pl/plpython/sql/plpython_error.sql        |   48 +-
 src/pl/plpython/sql/plpython_global.sql       |    6 +-
 src/pl/plpython/sql/plpython_import.sql       |    8 +-
 src/pl/plpython/sql/plpython_newline.sql      |    6 +-
 src/pl/plpython/sql/plpython_params.sql       |    8 +-
 src/pl/plpython/sql/plpython_quote.sql        |    2 +-
 src/pl/plpython/sql/plpython_record.sql       |   18 +-
 src/pl/plpython/sql/plpython_setof.sql        |   18 +-
 src/pl/plpython/sql/plpython_spi.sql          |   48 +-
 .../plpython/sql/plpython_subtransaction.sql  |   38 +-
 src/pl/plpython/sql/plpython_test.sql         |   12 +-
 src/pl/plpython/sql/plpython_transaction.sql  |   22 +-
 src/pl/plpython/sql/plpython_trigger.sql      |   46 +-
 src/pl/plpython/sql/plpython_types.sql        |  106 +-
 src/pl/plpython/sql/plpython_unicode.sql      |   16 +-
 src/pl/plpython/sql/plpython_void.sql         |    6 +-
 contrib/hstore_plpython/Makefile              |   10 +-
 .../expected/hstore_plpython.out              |   22 +-
 .../hstore_plpython2u--1.0.sql                |   19 -
 .../hstore_plpython/hstore_plpython2u.control |    6 -
 .../hstore_plpython/hstore_plpythonu--1.0.sql |   19 -
 .../hstore_plpython/hstore_plpythonu.control  |    6 -
 .../hstore_plpython/sql/hstore_plpython.sql   |   18 +-
 contrib/jsonb_plpython/Makefile               |   11 +-
 .../expected/jsonb_plpython.out               |   32 +-
 .../jsonb_plpython/jsonb_plpython2u--1.0.sql  |   19 -
 .../jsonb_plpython/jsonb_plpython2u.control   |    6 -
 .../jsonb_plpython/jsonb_plpythonu--1.0.sql   |   19 -
 .../jsonb_plpython/jsonb_plpythonu.control    |    6 -
 contrib/jsonb_plpython/sql/jsonb_plpython.sql |   30 +-
 contrib/ltree_plpython/Makefile               |   10 +-
 .../expected/ltree_plpython.out               |   10 +-
 .../ltree_plpython/ltree_plpython2u--1.0.sql  |   12 -
 .../ltree_plpython/ltree_plpython2u.control   |    6 -
 .../ltree_plpython/ltree_plpythonu--1.0.sql   |   12 -
 .../ltree_plpython/ltree_plpythonu.control    |    6 -
 contrib/ltree_plpython/sql/ltree_plpython.sql |    8 +-
 configure                                     |    4 +-
 82 files changed, 627 insertions(+), 2432 deletions(-)
 delete mode 100644 src/pl/plpython/expected/plpython_error_5.out
 delete mode 100644 src/pl/plpython/expected/plpython_types_3.out
 delete mode 100644 src/pl/plpython/plpython2u--1.0.sql
 delete mode 100644 src/pl/plpython/plpython2u.control
 delete mode 100644 src/pl/plpython/plpythonu--1.0.sql
 delete mode 100644 src/pl/plpython/plpythonu.control
 delete mode 100644 src/pl/plpython/regress-python3-mangle.mk
 delete mode 100644 contrib/hstore_plpython/hstore_plpython2u--1.0.sql
 delete mode 100644 contrib/hstore_plpython/hstore_plpython2u.control
 delete mode 100644 contrib/hstore_plpython/hstore_plpythonu--1.0.sql
 delete mode 100644 contrib/hstore_plpython/hstore_plpythonu.control
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpython2u.control
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpythonu.control
 delete mode 100644 contrib/ltree_plpython/ltree_plpython2u--1.0.sql
 delete mode 100644 contrib/ltree_plpython/ltree_plpython2u.control
 delete mode 100644 contrib/ltree_plpython/ltree_plpythonu--1.0.sql
 delete mode 100644 contrib/ltree_plpython/ltree_plpythonu.control

diff --git a/config/python.m4 b/config/python.m4
index c7310ee37d3..c4944a28a86 100644
--- a/config/python.m4
+++ b/config/python.m4
@@ -37,8 +37,8 @@ python_majorversion=`echo "$python_fullversion" | sed '[s/^\([0-9]*\).*/\1/]'`
 python_minorversion=`echo "$python_fullversion" | sed '[s/^[0-9]*\.\([0-9]*\).*/\1/]'`
 python_version=`echo "$python_fullversion" | sed '[s/^\([0-9]*\.[0-9]*\).*/\1/]'`
 # Reject unsupported Python versions as soon as practical.
-if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 7; then
-  AC_MSG_ERROR([Python version $python_version is too old (version 2.7 or later is required)])
+if test "$python_majorversion" -lt 3; then
+  AC_MSG_ERROR([Python version $python_version is too old (version 3 or later is required)])
 fi
 
 AC_MSG_CHECKING([for Python sysconfig module])
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index a83ae8865c7..0d6f74de71f 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -35,9 +35,6 @@ OBJS = \
 	plpy_util.o
 
 DATA = $(NAME)u.control $(NAME)u--1.0.sql
-ifeq ($(python_majorversion),2)
-DATA += plpythonu.control plpythonu--1.0.sql
-endif
 
 # header files to install - it's not clear which of these might be needed
 # so install them all.
@@ -77,11 +74,6 @@ endif # win32
 SHLIB_LINK = $(python_libspec) $(python_additional_libs) $(filter -lintl,$(LIBS))
 
 REGRESS_OPTS = --dbname=$(PL_TESTDB)
-# Only load plpythonu with Python 2.  The test files themselves load
-# the versioned language plpython(2|3)u.
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu
-endif
 
 REGRESS = \
 	plpython_schema \
@@ -108,8 +100,6 @@ REGRESS = \
 	plpython_transaction \
 	plpython_drop
 
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
-
 include $(top_srcdir)/src/Makefile.shlib
 
 all: all-lib
@@ -127,7 +117,6 @@ uninstall: uninstall-lib uninstall-data
 install-data: installdirs
 	$(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA)) '$(DESTDIR)$(datadir)/extension/'
 	$(INSTALL_DATA) $(addprefix $(srcdir)/, $(INCS)) '$(DESTDIR)$(includedir_server)'
-	$(INSTALL_DATA) $(srcdir)/regress-python3-mangle.mk '$(DESTDIR)$(pgxsdir)/src/pl/plpython'
 
 uninstall-data:
 	rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
@@ -136,9 +125,6 @@ uninstall-data:
 .PHONY: install-data uninstall-data
 
 
-include $(srcdir)/regress-python3-mangle.mk
-
-
 check: submake-pg-regress
 	$(pg_regress_check) $(REGRESS_OPTS) $(REGRESS)
 
diff --git a/src/pl/plpython/expected/plpython_call.out b/src/pl/plpython/expected/plpython_call.out
index 55e1027246a..4c0690067a0 100644
--- a/src/pl/plpython/expected/plpython_call.out
+++ b/src/pl/plpython/expected/plpython_call.out
@@ -2,14 +2,14 @@
 -- Tests for procedures / CALL syntax
 --
 CREATE PROCEDURE test_proc1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 pass
 $$;
 CALL test_proc1();
 -- error: can't return non-None
 CREATE PROCEDURE test_proc2()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return 5
 $$;
@@ -18,7 +18,7 @@ ERROR:  PL/Python procedure did not return None
 CONTEXT:  PL/Python procedure "test_proc2"
 CREATE TABLE test1 (a int);
 CREATE PROCEDURE test_proc3(x int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("INSERT INTO test1 VALUES (%s)" % x)
 $$;
@@ -31,7 +31,7 @@ SELECT * FROM test1;
 
 -- output arguments
 CREATE PROCEDURE test_proc5(INOUT a text)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return [a + '+' + a]
 $$;
@@ -42,7 +42,7 @@ CALL test_proc5('abc');
 (1 row)
 
 CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return (b * a, c * a)
 $$;
@@ -54,7 +54,7 @@ CALL test_proc6(2, 3, 4);
 
 -- OUT parameters
 CREATE PROCEDURE test_proc9(IN a int, OUT b int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.notice("a: %s" % (a))
 return (a * 2,)
diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out
index af801923343..bb101e07d53 100644
--- a/src/pl/plpython/expected/plpython_composite.out
+++ b/src/pl/plpython/expected/plpython_composite.out
@@ -1,6 +1,6 @@
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT multiout_simple();
  multiout_simple 
 -----------------
@@ -27,7 +27,7 @@ SELECT (multiout_simple()).j + 3;
 
 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT multiout_simple_setof();
  multiout_simple_setof 
 -----------------------
@@ -67,7 +67,7 @@ elif typ == 'obj':
     return type_record
 elif typ == 'str':
     return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
  first | second 
 -------+--------
@@ -237,7 +237,7 @@ for i in range(n):
     power = 2 ** i
     length = plpy.execute("select length('%d')" % power)[0]['length']
     yield power, length
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_setof(3);
  power_of_2 | length 
 ------------+--------
@@ -260,7 +260,7 @@ CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
 return [{'x': 4, 'y' :'four'},
         {'x': 7, 'y' :'seven'},
         {'x': 0, 'y' :'zero'}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_return_table();
  x |   y   
 ---+-------
@@ -273,7 +273,7 @@ CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS
 yield [[1], 'a']
 yield [[1,2], 'b']
 yield [[1,2,3], None]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_array();
  column1 | column2 
 ---------+---------
@@ -284,11 +284,11 @@ SELECT * FROM multiout_array();
 
 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
 return {'first': 1, 'second': 2}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
 return [{'first': 1, 'second': 2},
        {'first': 3, 'second': 4	}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM singleout_composite();
  first | second 
 -------+--------
@@ -305,7 +305,7 @@ SELECT * FROM multiout_composite();
 -- composite OUT parameters in functions returning RECORD not supported yet
 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
 return (n, (n * 2, n * 3))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
 if returnnull:
     d = None
@@ -323,7 +323,7 @@ elif typ == 'str':
     d = "(%r,%r)" % (n * 2, n * 3)
 for i in range(n):
     yield (i, d)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_composite(2);
  n | column2 
 ---+---------
@@ -438,7 +438,7 @@ CREATE TABLE changing (
 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
 return [(1, {'i': 1, 'j': 2}),
         (1, (3, 4))]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM changing_test();
  n | column2 
 ---+---------
@@ -474,7 +474,7 @@ yield {'tab': [('first', 1), ('second', 2)],
 yield {'tab': [('first', 1), ('second', 2)],
       'typ': [{'first': 'third', 'second': 3},
               {'first': 'fourth', 'second': 4}]}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_types_table();
             tab             |            typ             
 ----------------------------+----------------------------
@@ -486,7 +486,7 @@ SELECT * FROM composite_types_table();
 -- check what happens if the output record descriptor changes
 CREATE FUNCTION return_record(t text) RETURNS record AS $$
 return {'t': t, 'val': 10}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM return_record('abc') AS r(t text, val integer);
   t  | val 
 -----+-----
@@ -525,7 +525,7 @@ SELECT * FROM return_record('999') AS r(val text, t integer);
 
 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
 return {'v1':1,'v2':2,t:3}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
  v3 | v2 | v1 
 ----+----+----
@@ -572,7 +572,7 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
 -- multi-dimensional array of composite types.
 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list();
                                composite_type_as_list                               
 ------------------------------------------------------------------------------------
@@ -585,7 +585,7 @@ SELECT * FROM composite_type_as_list();
 -- on the issue.
 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
   return [['first', 1]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list_broken();
 ERROR:  malformed record literal: "first"
 DETAIL:  Missing left parenthesis.
diff --git a/src/pl/plpython/expected/plpython_do.out b/src/pl/plpython/expected/plpython_do.out
index e300530e031..d131a4c0ed6 100644
--- a/src/pl/plpython/expected/plpython_do.out
+++ b/src/pl/plpython/expected/plpython_do.out
@@ -1,8 +1,6 @@
-DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
-NOTICE:  This is plpythonu.
-DO $$ plpy.notice("This is plpython2u.") $$ LANGUAGE plpython2u;
-NOTICE:  This is plpython2u.
-DO $$ raise Exception("error test") $$ LANGUAGE plpythonu;
+DO $$ plpy.notice("This is plpython3u.") $$ LANGUAGE plpython3u;
+NOTICE:  This is plpython3u.
+DO $$ raise Exception("error test") $$ LANGUAGE plpython3u;
 ERROR:  Exception: error test
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
diff --git a/src/pl/plpython/expected/plpython_drop.out b/src/pl/plpython/expected/plpython_drop.out
index a0e3b5c4ef6..97bb54a55e7 100644
--- a/src/pl/plpython/expected/plpython_drop.out
+++ b/src/pl/plpython/expected/plpython_drop.out
@@ -2,5 +2,4 @@
 -- For paranoia's sake, don't leave an untrusted language sitting around
 --
 SET client_min_messages = WARNING;
-DROP EXTENSION plpythonu CASCADE;
-DROP EXTENSION IF EXISTS plpython2u CASCADE;
+DROP EXTENSION plpython3u CASCADE;
diff --git a/src/pl/plpython/expected/plpython_ereport.out b/src/pl/plpython/expected/plpython_ereport.out
index b73bfff5115..b38bb91e894 100644
--- a/src/pl/plpython/expected/plpython_ereport.out
+++ b/src/pl/plpython/expected/plpython_ereport.out
@@ -17,7 +17,7 @@ plpy.info('This is message text.',
 plpy.notice('notice', detail='some detail')
 plpy.warning('warning', detail='some detail')
 plpy.error('stop on error', detail='some detail', hint='some hint')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT elog_test();
 INFO:  info
 DETAIL:  some detail
@@ -38,42 +38,42 @@ CONTEXT:  Traceback (most recent call last):
   PL/Python function "elog_test", line 18, in <module>
     plpy.error('stop on error', detail='some detail', hint='some hint')
 PL/Python function "elog_test"
-DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpythonu;
+DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  (10, 20)
 DO $$
 import time;
 from datetime import date
 plpy.info('other types', detail=date(2016, 2, 26))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  2016-02-26
 DO $$
 basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 plpy.info('other types', detail=basket)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 -- should fail
-DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u;
 ERROR:  ValueError: invalid SQLSTATE code
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('wrong sqlstate', sqlstate='54444A') 
 PL/Python anonymous code block
-DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: 'blabla' is an invalid keyword argument for this function
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('unsupported argument', blabla='fooboo') 
 PL/Python anonymous code block
-DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: argument 'message' given by name and position
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('first message', message='second message') 
 PL/Python anonymous code block
-DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: argument 'message' given by name and position
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
@@ -96,7 +96,7 @@ kwargs = {
 }
 # ignore None values
 plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT raise_exception('hello', 'world');
 ERROR:  plpy.Error: hello
 DETAIL:  world
@@ -189,7 +189,7 @@ try:
 except Exception as e:
     plpy.info(e.spidata)
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  (119577128, None, 'some hint', None, 0, None, 'users_tab', None, 'user_type', None)
 ERROR:  plpy.SPIError: plpy.Error: my message
 HINT:  some hint
@@ -199,7 +199,7 @@ try:
 except Exception as e:
     plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name))
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  sqlstate: XX987, hint: some hint, table_name: users_tab, datatype_name: user_type
 ERROR:  plpy.Error: my message
 HINT:  some hint
diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index b2f8fe83eb6..7fe864a1a57 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -6,7 +6,7 @@
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 ERROR:  could not compile PL/Python function "python_syntax_error"
 DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
 /* With check_function_bodies = false the function should get defined
@@ -16,7 +16,7 @@ SET check_function_bodies = false;
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 SELECT python_syntax_error();
 ERROR:  could not compile PL/Python function "python_syntax_error"
 DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
@@ -30,7 +30,7 @@ RESET check_function_bodies;
 CREATE FUNCTION sql_syntax_error() RETURNS text
         AS
 'plpy.execute("syntax error")'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 SELECT sql_syntax_error();
 ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
 LINE 1: syntax error
@@ -45,7 +45,7 @@ PL/Python function "sql_syntax_error"
 CREATE FUNCTION exception_index_invalid(text) RETURNS text
 	AS
 'return args[1]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT exception_index_invalid('test');
 ERROR:  IndexError: list index out of range
 CONTEXT:  Traceback (most recent call last):
@@ -58,7 +58,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text
 	AS
 'rv = plpy.execute("SELECT test5(''foo'')")
 return rv[0]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT exception_index_invalid_nested();
 ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
 LINE 1: SELECT test5('foo')
@@ -81,7 +81,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_uncaught('rick');
 ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
 CONTEXT:  Traceback (most recent call last):
@@ -105,7 +105,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_caught('rick');
 NOTICE:  type "test" does not exist
  invalid_type_caught 
@@ -129,7 +129,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_reraised('rick');
 ERROR:  plpy.Error: type "test" does not exist
 CONTEXT:  Traceback (most recent call last):
@@ -147,7 +147,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT valid_type('rick');
  valid_type 
 ------------
@@ -170,7 +170,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_error();
 ERROR:  plpy.Error: boom
 CONTEXT:  Traceback (most recent call last):
@@ -199,7 +199,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_error_raise();
 ERROR:  plpy.Error: boom
 CONTEXT:  Traceback (most recent call last):
@@ -228,7 +228,7 @@ def fun3():
 fun3()
 return "you''ve been warned"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_warning();
 WARNING:  boom
    nested_warning   
@@ -241,9 +241,9 @@ WARNING:  boom
 CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
 $$
 plpy.nonexistent
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT toplevel_attribute_error();
-ERROR:  AttributeError: 'module' object has no attribute 'nonexistent'
+ERROR:  AttributeError: module 'plpy' has no attribute 'nonexistent'
 CONTEXT:  Traceback (most recent call last):
   PL/Python function "toplevel_attribute_error", line 2, in <module>
     plpy.nonexistent
@@ -261,7 +261,7 @@ def third():
   plpy.execute("select sql_error()")
 
 first()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
 begin
   select 1/0;
@@ -274,7 +274,7 @@ end
 $$ LANGUAGE plpgsql;
 CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
 plpy.execute("select sql_error()")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT python_traceback();
 ERROR:  spiexceptions.DivisionByZero: division by zero
 CONTEXT:  Traceback (most recent call last):
@@ -325,7 +325,7 @@ except spiexceptions.NotNullViolation as e:
     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
 except spiexceptions.UniqueViolation as e:
     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT specific_exception(2);
  specific_exception 
 --------------------
@@ -351,7 +351,7 @@ NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
 CREATE FUNCTION python_unique_violation() RETURNS void AS $$
 plpy.execute("insert into specific values (1)")
 plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
 begin
     begin
@@ -374,7 +374,7 @@ CREATE FUNCTION manual_subxact() RETURNS void AS $$
 plpy.execute("savepoint save")
 plpy.execute("create table foo(x integer)")
 plpy.execute("rollback to save")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT manual_subxact();
 ERROR:  plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
 CONTEXT:  Traceback (most recent call last):
@@ -389,7 +389,7 @@ rollback = plpy.prepare("rollback to save")
 plpy.execute(save)
 plpy.execute("create table foo(x integer)")
 plpy.execute(rollback)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT manual_subxact_prepared();
 ERROR:  plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
 CONTEXT:  Traceback (most recent call last):
@@ -400,7 +400,7 @@ PL/Python function "manual_subxact_prepared"
  */
 CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
 raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 DO $$
 BEGIN
 	SELECT plpy_raise_spiexception();
@@ -414,7 +414,7 @@ CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
 exc = plpy.spiexceptions.DivisionByZero()
 exc.sqlstate = 'SILLY'
 raise exc
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 DO $$
 BEGIN
 	SELECT plpy_raise_spiexception_override();
@@ -425,18 +425,18 @@ $$ LANGUAGE plpgsql;
 /* test the context stack trace for nested execution levels
  */
 CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
 plpy.execute("SELECT notice_innerfunc()")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 \set SHOW_CONTEXT always
 SELECT notice_outerfunc();
 NOTICE:  inside DO
 CONTEXT:  PL/Python anonymous code block
-SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
+SQL statement "DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$"
 PL/Python function "notice_innerfunc"
 SQL statement "SELECT notice_innerfunc()"
 PL/Python function "notice_outerfunc"
diff --git a/src/pl/plpython/expected/plpython_error_5.out b/src/pl/plpython/expected/plpython_error_5.out
deleted file mode 100644
index bc66ab55340..00000000000
--- a/src/pl/plpython/expected/plpython_error_5.out
+++ /dev/null
@@ -1,447 +0,0 @@
--- test error handling, i forgot to restore Warn_restart in
--- the trigger handler once. the errors and subsequent core dump were
--- interesting.
-/* Flat out Python syntax error
- */
-CREATE FUNCTION python_syntax_error() RETURNS text
-        AS
-'.syntaxerror'
-        LANGUAGE plpython3u;
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-/* With check_function_bodies = false the function should get defined
- * and the error reported when called
- */
-SET check_function_bodies = false;
-CREATE FUNCTION python_syntax_error() RETURNS text
-        AS
-'.syntaxerror'
-        LANGUAGE plpython3u;
-SELECT python_syntax_error();
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-/* Run the function twice to check if the hashtable entry gets cleaned up */
-SELECT python_syntax_error();
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-RESET check_function_bodies;
-/* Flat out syntax error
- */
-CREATE FUNCTION sql_syntax_error() RETURNS text
-        AS
-'plpy.execute("syntax error")'
-        LANGUAGE plpython3u;
-SELECT sql_syntax_error();
-ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
-LINE 1: syntax error
-        ^
-QUERY:  syntax error
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "sql_syntax_error", line 1, in <module>
-    plpy.execute("syntax error")
-PL/Python function "sql_syntax_error"
-/* check the handling of uncaught python exceptions
- */
-CREATE FUNCTION exception_index_invalid(text) RETURNS text
-	AS
-'return args[1]'
-	LANGUAGE plpython3u;
-SELECT exception_index_invalid('test');
-ERROR:  IndexError: list index out of range
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "exception_index_invalid", line 1, in <module>
-    return args[1]
-PL/Python function "exception_index_invalid"
-/* check handling of nested exceptions
- */
-CREATE FUNCTION exception_index_invalid_nested() RETURNS text
-	AS
-'rv = plpy.execute("SELECT test5(''foo'')")
-return rv[0]'
-	LANGUAGE plpython3u;
-SELECT exception_index_invalid_nested();
-ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
-LINE 1: SELECT test5('foo')
-               ^
-HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
-QUERY:  SELECT test5('foo')
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "exception_index_invalid_nested", line 1, in <module>
-    rv = plpy.execute("SELECT test5('foo')")
-PL/Python function "exception_index_invalid_nested"
-/* a typo
- */
-CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	SD["plan"] = plpy.prepare(q, [ "test" ])
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_uncaught('rick');
-ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "invalid_type_uncaught", line 3, in <module>
-    SD["plan"] = plpy.prepare(q, [ "test" ])
-PL/Python function "invalid_type_uncaught"
-/* for what it's worth catch the exception generated by
- * the typo, and return None
- */
-CREATE FUNCTION invalid_type_caught(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	try:
-		SD["plan"] = plpy.prepare(q, [ "test" ])
-	except plpy.SPIError as ex:
-		plpy.notice(str(ex))
-		return None
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_caught('rick');
-NOTICE:  type "test" does not exist
- invalid_type_caught 
----------------------
- 
-(1 row)
-
-/* for what it's worth catch the exception generated by
- * the typo, and reraise it as a plain error
- */
-CREATE FUNCTION invalid_type_reraised(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	try:
-		SD["plan"] = plpy.prepare(q, [ "test" ])
-	except plpy.SPIError as ex:
-		plpy.error(str(ex))
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_reraised('rick');
-ERROR:  plpy.Error: type "test" does not exist
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "invalid_type_reraised", line 6, in <module>
-    plpy.error(str(ex))
-PL/Python function "invalid_type_reraised"
-/* no typo no messing about
- */
-CREATE FUNCTION valid_type(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ])
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT valid_type('rick');
- valid_type 
-------------
- 
-(1 row)
-
-/* error in nested functions to get a traceback
-*/
-CREATE FUNCTION nested_error() RETURNS text
-	AS
-'def fun1():
-	plpy.error("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "not reached"
-'
-	LANGUAGE plpython3u;
-SELECT nested_error();
-ERROR:  plpy.Error: boom
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "nested_error", line 10, in <module>
-    fun3()
-  PL/Python function "nested_error", line 8, in fun3
-    fun2()
-  PL/Python function "nested_error", line 5, in fun2
-    fun1()
-  PL/Python function "nested_error", line 2, in fun1
-    plpy.error("boom")
-PL/Python function "nested_error"
-/* raising plpy.Error is just like calling plpy.error
-*/
-CREATE FUNCTION nested_error_raise() RETURNS text
-	AS
-'def fun1():
-	raise plpy.Error("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "not reached"
-'
-	LANGUAGE plpython3u;
-SELECT nested_error_raise();
-ERROR:  plpy.Error: boom
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "nested_error_raise", line 10, in <module>
-    fun3()
-  PL/Python function "nested_error_raise", line 8, in fun3
-    fun2()
-  PL/Python function "nested_error_raise", line 5, in fun2
-    fun1()
-  PL/Python function "nested_error_raise", line 2, in fun1
-    raise plpy.Error("boom")
-PL/Python function "nested_error_raise"
-/* using plpy.warning should not produce a traceback
-*/
-CREATE FUNCTION nested_warning() RETURNS text
-	AS
-'def fun1():
-	plpy.warning("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "you''ve been warned"
-'
-	LANGUAGE plpython3u;
-SELECT nested_warning();
-WARNING:  boom
-   nested_warning   
---------------------
- you've been warned
-(1 row)
-
-/* AttributeError at toplevel used to give segfaults with the traceback
-*/
-CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
-$$
-plpy.nonexistent
-$$ LANGUAGE plpython3u;
-SELECT toplevel_attribute_error();
-ERROR:  AttributeError: module 'plpy' has no attribute 'nonexistent'
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "toplevel_attribute_error", line 2, in <module>
-    plpy.nonexistent
-PL/Python function "toplevel_attribute_error"
-/* Calling PL/Python functions from SQL and vice versa should not lose context.
- */
-CREATE OR REPLACE FUNCTION python_traceback() RETURNS void AS $$
-def first():
-  second()
-
-def second():
-  third()
-
-def third():
-  plpy.execute("select sql_error()")
-
-first()
-$$ LANGUAGE plpython3u;
-CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
-begin
-  select 1/0;
-end
-$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION python_from_sql_error() RETURNS void AS $$
-begin
-  select python_traceback();
-end
-$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
-plpy.execute("select sql_error()")
-$$ LANGUAGE plpython3u;
-SELECT python_traceback();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "python_traceback", line 11, in <module>
-    first()
-  PL/Python function "python_traceback", line 3, in first
-    second()
-  PL/Python function "python_traceback", line 6, in second
-    third()
-  PL/Python function "python_traceback", line 9, in third
-    plpy.execute("select sql_error()")
-PL/Python function "python_traceback"
-SELECT sql_error();
-ERROR:  division by zero
-CONTEXT:  SQL statement "select 1/0"
-PL/pgSQL function sql_error() line 3 at SQL statement
-SELECT python_from_sql_error();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "python_traceback", line 11, in <module>
-    first()
-  PL/Python function "python_traceback", line 3, in first
-    second()
-  PL/Python function "python_traceback", line 6, in second
-    third()
-  PL/Python function "python_traceback", line 9, in third
-    plpy.execute("select sql_error()")
-PL/Python function "python_traceback"
-SQL statement "select python_traceback()"
-PL/pgSQL function python_from_sql_error() line 3 at SQL statement
-SELECT sql_from_python_error();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "sql_from_python_error", line 2, in <module>
-    plpy.execute("select sql_error()")
-PL/Python function "sql_from_python_error"
-/* check catching specific types of exceptions
- */
-CREATE TABLE specific (
-    i integer PRIMARY KEY
-);
-CREATE FUNCTION specific_exception(i integer) RETURNS void AS
-$$
-from plpy import spiexceptions
-try:
-    plpy.execute("insert into specific values (%s)" % (i or "NULL"));
-except spiexceptions.NotNullViolation as e:
-    plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
-except spiexceptions.UniqueViolation as e:
-    plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpython3u;
-SELECT specific_exception(2);
- specific_exception 
---------------------
- 
-(1 row)
-
-SELECT specific_exception(NULL);
-NOTICE:  Violated the NOT NULL constraint, sqlstate 23502
- specific_exception 
---------------------
- 
-(1 row)
-
-SELECT specific_exception(2);
-NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
- specific_exception 
---------------------
- 
-(1 row)
-
-/* SPI errors in PL/Python functions should preserve the SQLSTATE value
- */
-CREATE FUNCTION python_unique_violation() RETURNS void AS $$
-plpy.execute("insert into specific values (1)")
-plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpython3u;
-CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
-begin
-    begin
-        perform python_unique_violation();
-    exception when unique_violation then
-        return 'ok';
-    end;
-    return 'not reached';
-end;
-$$ language plpgsql;
-SELECT catch_python_unique_violation();
- catch_python_unique_violation 
--------------------------------
- ok
-(1 row)
-
-/* manually starting subtransactions - a bad idea
- */
-CREATE FUNCTION manual_subxact() RETURNS void AS $$
-plpy.execute("savepoint save")
-plpy.execute("create table foo(x integer)")
-plpy.execute("rollback to save")
-$$ LANGUAGE plpython3u;
-SELECT manual_subxact();
-ERROR:  plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "manual_subxact", line 2, in <module>
-    plpy.execute("savepoint save")
-PL/Python function "manual_subxact"
-/* same for prepared plans
- */
-CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
-save = plpy.prepare("savepoint save")
-rollback = plpy.prepare("rollback to save")
-plpy.execute(save)
-plpy.execute("create table foo(x integer)")
-plpy.execute(rollback)
-$$ LANGUAGE plpython3u;
-SELECT manual_subxact_prepared();
-ERROR:  plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "manual_subxact_prepared", line 4, in <module>
-    plpy.execute(save)
-PL/Python function "manual_subxact_prepared"
-/* raising plpy.spiexception.* from python code should preserve sqlstate
- */
-CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
-raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpython3u;
-DO $$
-BEGIN
-	SELECT plpy_raise_spiexception();
-EXCEPTION WHEN division_by_zero THEN
-	-- NOOP
-END
-$$ LANGUAGE plpgsql;
-/* setting a custom sqlstate should be handled
- */
-CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
-exc = plpy.spiexceptions.DivisionByZero()
-exc.sqlstate = 'SILLY'
-raise exc
-$$ LANGUAGE plpython3u;
-DO $$
-BEGIN
-	SELECT plpy_raise_spiexception_override();
-EXCEPTION WHEN SQLSTATE 'SILLY' THEN
-	-- NOOP
-END
-$$ LANGUAGE plpgsql;
-/* test the context stack trace for nested execution levels
- */
-CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
-return 1
-$$ LANGUAGE plpythonu;
-CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
-plpy.execute("SELECT notice_innerfunc()")
-return 1
-$$ LANGUAGE plpythonu;
-\set SHOW_CONTEXT always
-SELECT notice_outerfunc();
-NOTICE:  inside DO
-CONTEXT:  PL/Python anonymous code block
-SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
-PL/Python function "notice_innerfunc"
-SQL statement "SELECT notice_innerfunc()"
-PL/Python function "notice_outerfunc"
- notice_outerfunc 
-------------------
-                1
-(1 row)
-
diff --git a/src/pl/plpython/expected/plpython_global.out b/src/pl/plpython/expected/plpython_global.out
index 192e3e48a72..a4cfb1483f9 100644
--- a/src/pl/plpython/expected/plpython_global.out
+++ b/src/pl/plpython/expected/plpython_global.out
@@ -8,7 +8,7 @@ CREATE FUNCTION global_test_one() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_one"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION global_test_two() returns text
     AS
 'if "global_test" not in SD:
@@ -16,7 +16,7 @@ CREATE FUNCTION global_test_two() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_two"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION static_test() returns int4
     AS
 'if "call" in SD:
@@ -25,7 +25,7 @@ else:
 	SD["call"] = 1
 return SD["call"]
 '
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 SELECT static_test();
  static_test 
 -------------
diff --git a/src/pl/plpython/expected/plpython_import.out b/src/pl/plpython/expected/plpython_import.out
index b59e1821a79..854e989eaf9 100644
--- a/src/pl/plpython/expected/plpython_import.out
+++ b/src/pl/plpython/expected/plpython_import.out
@@ -6,7 +6,7 @@ CREATE FUNCTION import_fail() returns text
 except ImportError:
 	return "failed as expected"
 return "succeeded, that wasn''t supposed to happen"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION import_succeed() returns text
 	AS
 'try:
@@ -25,7 +25,7 @@ except Exception as ex:
 	plpy.notice("import failed -- %s" % str(ex))
 	return "failed, that wasn''t supposed to happen"
 return "succeeded, as expected"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION import_test_one(p text) RETURNS text
 	AS
 'try:
@@ -35,7 +35,7 @@ except ImportError:
     import sha
     digest = sha.new(p)
 return digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION import_test_two(u users) RETURNS text
 	AS
 'plain = u["fname"] + u["lname"]
@@ -46,7 +46,7 @@ except ImportError:
     import sha
     digest = sha.new(plain);
 return "sha hash of " + plain + " is " + digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 -- import python modules
 --
 SELECT import_fail();
diff --git a/src/pl/plpython/expected/plpython_newline.out b/src/pl/plpython/expected/plpython_newline.out
index 27dc2f8ab0c..2bc149257e7 100644
--- a/src/pl/plpython/expected/plpython_newline.out
+++ b/src/pl/plpython/expected/plpython_newline.out
@@ -3,13 +3,13 @@
 --
 CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS
 E'x = 100\ny = 23\nreturn x + y\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS
 E'x = 100\ry = 23\rreturn x + y\r'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS
 E'x = 100\r\ny = 23\r\nreturn x + y\r\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 SELECT newline_lf();
  newline_lf 
 ------------
diff --git a/src/pl/plpython/expected/plpython_params.out b/src/pl/plpython/expected/plpython_params.out
index 46ea7dfb90b..d1a36f36239 100644
--- a/src/pl/plpython/expected/plpython_params.out
+++ b/src/pl/plpython/expected/plpython_params.out
@@ -3,12 +3,12 @@
 --
 CREATE FUNCTION test_param_names0(integer, integer) RETURNS int AS $$
 return args[0] + args[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_param_names1(a0 integer, a1 text) RETURNS boolean AS $$
 assert a0 == args[0]
 assert a1 == args[1]
 return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_param_names2(u users) RETURNS text AS $$
 assert u == args[0]
 if isinstance(u, dict):
@@ -19,7 +19,7 @@ if isinstance(u, dict):
 else:
     s = str(u)
 return s
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- use deliberately wrong parameter names
 CREATE FUNCTION test_param_names3(a0 integer) RETURNS boolean AS $$
 try:
@@ -28,7 +28,7 @@ try:
 except NameError as e:
 	assert e.args[0].find("a1") > -1
 	return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_param_names0(2,7);
  test_param_names0 
 -------------------
diff --git a/src/pl/plpython/expected/plpython_quote.out b/src/pl/plpython/expected/plpython_quote.out
index eed72923aec..1fbe93d5351 100644
--- a/src/pl/plpython/expected/plpython_quote.out
+++ b/src/pl/plpython/expected/plpython_quote.out
@@ -8,7 +8,7 @@ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
         return plpy.quote_ident(t)
     else:
         raise plpy.Error("unrecognized quote type %s" % how)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT quote(t, 'literal') FROM (VALUES
        ('abc'),
        ('a''bc'),
diff --git a/src/pl/plpython/expected/plpython_record.out b/src/pl/plpython/expected/plpython_record.out
index 458330713a8..31de198582b 100644
--- a/src/pl/plpython/expected/plpython_record.out
+++ b/src/pl/plpython/expected/plpython_record.out
@@ -23,7 +23,7 @@ elif typ == 'obj':
 	type_record.first = first
 	type_record.second = second
 	return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
 if retnull:
 	return None
@@ -40,17 +40,17 @@ elif typ == 'obj':
 	return type_record
 elif typ == 'str':
 	return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
 return first + '_in_to_out';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- Test tuple returning functions
 SELECT * FROM test_table_record_as('dict', null, null, false);
  first | second 
@@ -340,7 +340,7 @@ SELECT * FROM test_type_record_as('obj', 'one', 1, false);
 -- errors cases
 CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
     return { 'first': 'first' }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error1();
 ERROR:  key "second" not found in mapping
 HINT:  To return null in a column, add the value None to the mapping with the key named after the column.
@@ -348,7 +348,7 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_record_error1"
 CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
     return [ 'first' ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error2();
 ERROR:  length of returned sequence did not match number of columns in row
 CONTEXT:  while creating return value
@@ -357,7 +357,7 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
     class type_record: pass
     type_record.first = 'first'
     return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error3();
 ERROR:  attribute "second" does not exist in Python object
 HINT:  To return null in a column, let the returned object have an attribute named after column with value None.
@@ -365,7 +365,7 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_record_error3"
 CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
     return 'foo'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error4();
 ERROR:  malformed record literal: "foo"
 DETAIL:  Missing left parenthesis.
diff --git a/src/pl/plpython/expected/plpython_setof.out b/src/pl/plpython/expected/plpython_setof.out
index 170dbc394de..39409400290 100644
--- a/src/pl/plpython/expected/plpython_setof.out
+++ b/src/pl/plpython/expected/plpython_setof.out
@@ -3,20 +3,20 @@
 --
 CREATE FUNCTION test_setof_error() RETURNS SETOF text AS $$
 return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_setof_error();
 ERROR:  returned object cannot be iterated
 DETAIL:  PL/Python set-returning functions must return an iterable object.
 CONTEXT:  PL/Python function "test_setof_error"
 CREATE FUNCTION test_setof_as_list(count integer, content text) RETURNS SETOF text AS $$
 return [ content ]*count
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_as_tuple(count integer, content text) RETURNS SETOF text AS $$
 t = ()
 for i in range(count):
 	t += ( content, )
 return t
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_as_iterator(count integer, content text) RETURNS SETOF text AS $$
 class producer:
 	def __init__ (self, icount, icontent):
@@ -24,13 +24,13 @@ class producer:
 		self.icount = icount
 	def __iter__ (self):
 		return self
-	def next (self):
+	def __next__ (self):
 		if self.icount == 0:
 			raise StopIteration
 		self.icount -= 1
 		return self.icontent
 return producer(count, content)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_spi_in_iterator() RETURNS SETOF text AS
 $$
     for s in ('Hello', 'Brave', 'New', 'World'):
@@ -38,7 +38,7 @@ $$
         yield s
         plpy.execute('select 2')
 $$
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 -- Test set returning functions
 SELECT test_setof_as_list(0, 'list');
  test_setof_as_list 
@@ -130,7 +130,7 @@ global x
 while x <= lim:
     yield x
     x = x + 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT ugly(1, 5);
  ugly 
 ------
@@ -155,7 +155,7 @@ CREATE OR REPLACE FUNCTION get_user_records()
 RETURNS SETOF users
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT get_user_records();
    get_user_records   
 ----------------------
@@ -179,7 +179,7 @@ CREATE OR REPLACE FUNCTION get_user_records2()
 RETURNS TABLE(fname text, lname text, username text, userid int)
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT get_user_records2();
   get_user_records2   
 ----------------------
diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out
index a09df68c7d1..391fdb0e645 100644
--- a/src/pl/plpython/expected/plpython_spi.out
+++ b/src/pl/plpython/expected/plpython_spi.out
@@ -6,17 +6,17 @@ CREATE FUNCTION nested_call_one(a text) RETURNS text
 'q = "SELECT nested_call_two(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 CREATE FUNCTION nested_call_two(a text) RETURNS text
 	AS
 'q = "SELECT nested_call_three(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 CREATE FUNCTION nested_call_three(a text) RETURNS text
 	AS
 'return a'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 -- some spi stuff
 CREATE FUNCTION spi_prepared_plan_test_one(a text) RETURNS text
 	AS
@@ -30,7 +30,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
 	AS
 'if "myplan" not in SD:
@@ -43,7 +43,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
 	AS
 'if "myplan" not in SD:
@@ -57,7 +57,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION join_sequences(s sequences) RETURNS text
 	AS
 'if not s["multipart"]:
@@ -69,7 +69,7 @@ for r in rv:
 	seq = seq + r["sequence"]
 return seq
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
 	AS
 'r = 0
@@ -77,7 +77,7 @@ if a > 1:
     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
 return a + r
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 --
 -- spi and nested calls
 --
@@ -155,7 +155,7 @@ if result.status() > 0:
    return result.nrows()
 else:
    return None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
 INFO:  True
 INFO:  ['foo', 'bar']
@@ -177,7 +177,7 @@ CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return result.nrows()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_nrows_test($$SELECT 1$$);
  result_nrows_test 
 -------------------
@@ -206,7 +206,7 @@ CREATE FUNCTION result_len_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return len(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_len_test($$SELECT 1$$);
  result_len_test 
 -----------------
@@ -254,7 +254,7 @@ except TypeError:
 else:
     assert False, "TypeError not raised"
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_subscript_test();
 INFO:  2
 INFO:  4
@@ -272,7 +272,7 @@ result = plpy.execute("select 1 where false")
 
 plpy.info(result[:])
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_empty_test();
 INFO:  []
  result_empty_test 
@@ -285,7 +285,7 @@ AS $$
 plan = plpy.prepare(cmd)
 result = plpy.execute(plan)
 return str(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
                       result_str_test                       
 ------------------------------------------------------------
@@ -306,12 +306,12 @@ for row in res:
     if row['lname'] == 'doe':
         does += 1
 return does
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
 res.close()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 assert len(res.fetch(3)) == 3
@@ -329,7 +329,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users order by fname")
 assert len(res.fetch(2)) == 2
@@ -342,7 +342,7 @@ except AttributeError:
 assert item['fname'] == 'rick'
 
 assert len(res.fetch(2)) == 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
@@ -352,7 +352,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION next_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
@@ -365,7 +365,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users where false")
 assert len(res.fetch(1)) == 0
@@ -378,7 +378,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
 plan = plpy.prepare(
     "select fname, lname from users where fname like $1 || '%' order by fname",
@@ -387,12 +387,12 @@ for row in plpy.cursor(plan, ["w"]):
     yield row['fname']
 for row in plan.cursor(["j"]):
     yield row['fname']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
                     ["text"])
 c = plpy.cursor(plan, ["a", "b"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TYPE test_composite_type AS (
   a1 int,
   a2 varchar
@@ -401,7 +401,7 @@ CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS
 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
 return res[0]["c1"]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT simple_cursor_test();
  simple_cursor_test 
 --------------------
diff --git a/src/pl/plpython/expected/plpython_subtransaction.out b/src/pl/plpython/expected/plpython_subtransaction.out
index 2a56541917d..43d9277a33b 100644
--- a/src/pl/plpython/expected/plpython_subtransaction.out
+++ b/src/pl/plpython/expected/plpython_subtransaction.out
@@ -14,7 +14,7 @@ with plpy.subtransaction():
         plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
     elif what_error == "Python":
         raise Exception("Python exception")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_ctx_test();
  subtransaction_ctx_test 
 -------------------------
@@ -71,7 +71,7 @@ with plpy.subtransaction():
             raise
         plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_nested_test();
 ERROR:  spiexceptions.SyntaxError: syntax error at or near "error"
 LINE 1: error
@@ -111,7 +111,7 @@ with plpy.subtransaction():
     plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
     plpy.execute("SELECT subtransaction_nested_test('t')")
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_deeply_nested_test();
 NOTICE:  Swallowed SyntaxError('syntax error at or near "error"')
  subtransaction_deeply_nested_test 
@@ -133,42 +133,42 @@ TRUNCATE subtransaction_tbl;
 CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
 AS $$
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__exit__(None, None, None)
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
 AS $$
 s = plpy.subtransaction()
 s.__enter__()
 s.__exit__(None, None, None)
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
 AS $$
 s = plpy.subtransaction()
 s.__enter__()
 s.__enter__()
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- No warnings here, as the subtransaction gets indeed closed
 CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
 AS $$
 with plpy.subtransaction() as s:
     s.__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
 AS $$
 try:
@@ -176,7 +176,7 @@ try:
         s.__exit__(None, None, None)
 except ValueError as e:
     raise ValueError(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_exit_without_enter();
 ERROR:  ValueError: this subtransaction has not been entered
 CONTEXT:  Traceback (most recent call last):
@@ -255,7 +255,7 @@ try:
     plpy.execute(p, ["wrong"])
 except plpy.SPIError:
     plpy.warning("Caught a SPI error")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_mix_explicit_and_implicit();
 WARNING:  Caught a SPI error from an explicit subtransaction
 WARNING:  Caught a SPI error
@@ -278,7 +278,7 @@ AS $$
 s = plpy.subtransaction()
 s.enter()
 s.exit(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_alternative_names();
  subtransaction_alternative_names 
 ----------------------------------
@@ -294,7 +294,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT try_catch_inside_subtransaction();
 NOTICE:  caught
  try_catch_inside_subtransaction 
@@ -318,7 +318,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT pk_violation_inside_subtransaction();
 NOTICE:  caught
  pk_violation_inside_subtransaction 
@@ -340,7 +340,7 @@ with plpy.subtransaction():
     cur.fetch(10)
 fetched = cur.fetch(10);
 return int(fetched[5]["i"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
 try:
     with plpy.subtransaction():
@@ -351,7 +351,7 @@ except plpy.SPIError:
     fetched = cur.fetch(10)
     return int(fetched[5]["i"])
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
 try:
     with plpy.subtransaction():
@@ -364,7 +364,7 @@ except plpy.SPIError:
     fetched = cur.fetch(5)
     return fetched[2]["i"]
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
 try:
     with plpy.subtransaction():
@@ -374,7 +374,7 @@ except plpy.SPIError:
     cur.close()
     return True
 return False # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT cursor_in_subxact();
  cursor_in_subxact 
 -------------------
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index 39b994f4468..13c14119c08 100644
--- a/src/pl/plpython/expected/plpython_test.out
+++ b/src/pl/plpython/expected/plpython_test.out
@@ -1,7 +1,7 @@
 -- first some tests of basic functionality
-CREATE EXTENSION plpython2u;
+CREATE EXTENSION plpython3u;
 -- really stupid function just to get the module loaded
-CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
+CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 select stupid();
  stupid 
 --------
@@ -9,7 +9,7 @@ select stupid();
 (1 row)
 
 -- check 2/3 versioning
-CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
+CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 select stupidn();
  stupidn 
 ---------
@@ -26,7 +26,7 @@ for key in keys:
     out.append("%s: %s" % (key, u[key]))
 words = a1 + " " + a2 + " => {" + ", ".join(out) + "}"
 return words'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 select "Argument test #1"(users, fname, lname) from users where lname = 'doe' order by 1;
                            Argument test #1                            
 -----------------------------------------------------------------------
@@ -41,7 +41,7 @@ $$
 contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
 contents.sort()
 return contents
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select module_contents();
  module_contents 
 -----------------
@@ -78,7 +78,7 @@ plpy.info('info', 37, [1, 2, 3])
 plpy.notice('notice')
 plpy.warning('warning')
 plpy.error('error')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT elog_test_basic();
 INFO:  info
 INFO:  37
diff --git a/src/pl/plpython/expected/plpython_transaction.out b/src/pl/plpython/expected/plpython_transaction.out
index 14152993c75..393ea21eaad 100644
--- a/src/pl/plpython/expected/plpython_transaction.out
+++ b/src/pl/plpython/expected/plpython_transaction.out
@@ -1,6 +1,6 @@
 CREATE TABLE test1 (a int, b text);
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -22,7 +22,7 @@ SELECT * FROM test1;
 
 TRUNCATE test1;
 DO
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -44,7 +44,7 @@ SELECT * FROM test1;
 TRUNCATE test1;
 -- not allowed in a function
 CREATE FUNCTION transaction_test2() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -64,7 +64,7 @@ SELECT * FROM test1;
 
 -- also not allowed if procedure is called from a function
 CREATE FUNCTION transaction_test3() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("CALL transaction_test1()")
 return 1
@@ -82,19 +82,19 @@ SELECT * FROM test1;
 
 -- DO block inside function
 CREATE FUNCTION transaction_test4() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 return 1
 $$;
 SELECT transaction_test4();
 ERROR:  spiexceptions.InvalidTransactionTermination: invalid transaction termination
 CONTEXT:  Traceback (most recent call last):
   PL/Python function "transaction_test4", line 2, in <module>
-    plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+    plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 PL/Python function "transaction_test4"
 -- commit inside subtransaction (prohibited)
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 s = plpy.subtransaction()
 s.enter()
 plpy.commit()
@@ -106,7 +106,7 @@ CONTEXT:  PL/Python anonymous code block
 CREATE TABLE test2 (x int);
 INSERT INTO test2 VALUES (0), (1), (2), (3), (4);
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.commit()
@@ -129,7 +129,7 @@ SELECT * FROM pg_cursors;
 
 -- error in cursor loop with commit
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x'])
     plpy.commit()
@@ -153,7 +153,7 @@ SELECT * FROM pg_cursors;
 
 -- rollback inside cursor loop
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.rollback()
@@ -170,7 +170,7 @@ SELECT * FROM pg_cursors;
 
 -- first commit then rollback inside cursor loop
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     if row['x'] % 2 == 0:
diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out
index 742988a5b59..dd1ca32fa49 100644
--- a/src/pl/plpython/expected/plpython_trigger.out
+++ b/src/pl/plpython/expected/plpython_trigger.out
@@ -15,20 +15,20 @@ if TD["new"]["fname"] == "william":
 	TD["new"]["fname"] = TD["args"][0]
 	rv = "MODIFY"
 return rv'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION users_update() returns trigger
 	AS
 'if TD["event"] == "UPDATE":
 	if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
 		return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION users_delete() RETURNS trigger
 	AS
 'if TD["old"]["fname"] == TD["args"][0]:
 	return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
 	EXECUTE PROCEDURE users_insert ('willem');
 CREATE TRIGGER users_update_trig BEFORE UPDATE ON users FOR EACH ROW
@@ -71,7 +71,7 @@ CREATE TABLE trigger_test_generated (
 	i int,
         j int GENERATED ALWAYS AS (i * 2) STORED
 );
-CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
 
 if 'relid' in TD:
 	TD['relid'] = "bogus:12345"
@@ -328,7 +328,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid1() RETURNS trigger
 AS $$
     return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger1
 BEFORE INSERT ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid1();
@@ -341,7 +341,7 @@ DROP TRIGGER stupid_trigger1 ON trigger_test;
 CREATE FUNCTION stupid2() RETURNS trigger
 AS $$
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger2
 BEFORE DELETE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid2();
@@ -353,7 +353,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid3() RETURNS trigger
 AS $$
     return "foo"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid3();
@@ -365,8 +365,8 @@ DROP TRIGGER stupid_trigger3 ON trigger_test;
 -- Unicode variant
 CREATE FUNCTION stupid3u() RETURNS trigger
 AS $$
-    return u"foo"
-$$ LANGUAGE plpythonu;
+    return "foo"
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid3u();
@@ -380,7 +380,7 @@ CREATE FUNCTION stupid4() RETURNS trigger
 AS $$
     del TD["new"]
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger4
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid4();
@@ -394,7 +394,7 @@ CREATE FUNCTION stupid5() RETURNS trigger
 AS $$
     TD["new"] = ['foo', 'bar']
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger5
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid5();
@@ -408,7 +408,7 @@ CREATE FUNCTION stupid6() RETURNS trigger
 AS $$
     TD["new"] = {1: 'foo', 2: 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger6
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid6();
@@ -422,7 +422,7 @@ CREATE FUNCTION stupid7() RETURNS trigger
 AS $$
     TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid7();
@@ -434,9 +434,9 @@ DROP TRIGGER stupid_trigger7 ON trigger_test;
 -- Unicode variant
 CREATE FUNCTION stupid7u() RETURNS trigger
 AS $$
-    TD["new"] = {u'v': 'foo', u'a': 'bar'}
+    TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid7u();
@@ -461,7 +461,7 @@ CREATE FUNCTION test_null() RETURNS trigger
 AS $$
     TD["new"]['v'] = None
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER test_null_trigger
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE test_null();
@@ -481,7 +481,7 @@ SET DateStyle = 'ISO';
 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
 CREATE TRIGGER set_modif_time BEFORE UPDATE ON pb
   FOR EACH ROW EXECUTE PROCEDURE set_modif_time();
@@ -507,7 +507,7 @@ CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
     TD['new']['f1'] = (3, False)
     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
 INSERT INTO composite_trigger_test VALUES (NULL, NULL);
@@ -521,7 +521,7 @@ SELECT * FROM composite_trigger_test;
 CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
 INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL);
@@ -540,7 +540,7 @@ CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer);
 CREATE TABLE composite_trigger_nested_test(c comp3);
 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
 INSERT INTO composite_trigger_nested_test VALUES (NULL);
@@ -555,7 +555,7 @@ SELECT * FROM composite_trigger_nested_test;
 (3 rows)
 
 -- check that using a function as a trigger over two tables works correctly
-CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
     TD["new"]["data"] = '1234'
     return 'MODIFY'
 $$;
@@ -581,7 +581,7 @@ SELECT * FROM b;
 -- check that SQL run in trigger code can see transition tables
 CREATE TABLE transition_table_test (id int, name text);
 INSERT INTO transition_table_test VALUES (1, 'a');
-CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
 $$
     rv = plpy.execute("SELECT * FROM old_table")
     assert(rv.nrows() == 1)
@@ -601,7 +601,7 @@ DROP TABLE transition_table_test;
 DROP FUNCTION transition_table_test_f();
 -- dealing with generated columns
 CREATE FUNCTION generated_test_func1() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 TD['new']['j'] = 5  # not allowed
 return 'MODIFY'
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index 0a2659fe292..a470911c2ec 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -7,23 +7,23 @@
 CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bool(true);
-INFO:  (True, <type 'bool'>)
+INFO:  (True, <class 'bool'>)
  test_type_conversion_bool 
 ---------------------------
  t
 (1 row)
 
 SELECT * FROM test_type_conversion_bool(false);
-INFO:  (False, <type 'bool'>)
+INFO:  (False, <class 'bool'>)
  test_type_conversion_bool 
 ---------------------------
  f
 (1 row)
 
 SELECT * FROM test_type_conversion_bool(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_bool 
 ---------------------------
  
@@ -48,7 +48,7 @@ elif n == 5:
    ret = [0]
 plpy.info(ret, not not ret)
 return ret
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bool_other(0);
 INFO:  (0, False)
  test_type_conversion_bool_other 
@@ -94,16 +94,16 @@ INFO:  ([0], True)
 CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_char('a');
-INFO:  ('a', <type 'str'>)
+INFO:  ('a', <class 'str'>)
  test_type_conversion_char 
 ---------------------------
  a
 (1 row)
 
 SELECT * FROM test_type_conversion_char(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_char 
 ---------------------------
  
@@ -112,23 +112,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int2(100::int2);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int2 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int2(-100::int2);
-INFO:  (-100, <type 'int'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int2 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int2(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int2 
 ---------------------------
                           
@@ -137,23 +137,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int4(100);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int4 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int4(-100);
-INFO:  (-100, <type 'int'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int4 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int4(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int4 
 ---------------------------
                           
@@ -162,30 +162,30 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int8(100);
-INFO:  (100L, <type 'long'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(-100);
-INFO:  (-100L, <type 'long'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(5000000000);
-INFO:  (5000000000L, <type 'long'>)
+INFO:  (5000000000, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                 5000000000
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int8 
 ---------------------------
                           
@@ -196,7 +196,7 @@ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
 # between decimal and cdecimal
 plpy.info(str(x), x.__class__.__name__)
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_numeric(100);
 INFO:  ('100', 'Decimal')
  test_type_conversion_numeric 
@@ -256,30 +256,30 @@ INFO:  ('None', 'NoneType')
 CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_float4(100);
-INFO:  (100.0, <type 'float'>)
+INFO:  (100.0, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                          100
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(-100);
-INFO:  (-100.0, <type 'float'>)
+INFO:  (-100.0, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                         -100
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(5000.5);
-INFO:  (5000.5, <type 'float'>)
+INFO:  (5000.5, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                       5000.5
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_float4 
 -----------------------------
                             
@@ -288,37 +288,37 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_float8(100);
-INFO:  (100.0, <type 'float'>)
+INFO:  (100.0, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                          100
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(-100);
-INFO:  (-100.0, <type 'float'>)
+INFO:  (-100.0, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                         -100
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(5000000000.5);
-INFO:  (5000000000.5, <type 'float'>)
+INFO:  (5000000000.5, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                 5000000000.5
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_float8 
 -----------------------------
                             
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(100100100.654321);
-INFO:  (100100100.654321, <type 'float'>)
+INFO:  (100100100.654321, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
             100100100.654321
@@ -327,23 +327,23 @@ INFO:  (100100100.654321, <type 'float'>)
 CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_oid(100);
-INFO:  (100L, <type 'long'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_oid 
 --------------------------
                       100
 (1 row)
 
 SELECT * FROM test_type_conversion_oid(2147483649);
-INFO:  (2147483649L, <type 'long'>)
+INFO:  (2147483649, <class 'int'>)
  test_type_conversion_oid 
 --------------------------
                2147483649
 (1 row)
 
 SELECT * FROM test_type_conversion_oid(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_oid 
 --------------------------
                          
@@ -352,16 +352,16 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_text('hello world');
-INFO:  ('hello world', <type 'str'>)
+INFO:  ('hello world', <class 'str'>)
  test_type_conversion_text 
 ---------------------------
  hello world
 (1 row)
 
 SELECT * FROM test_type_conversion_text(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_text 
 ---------------------------
  
@@ -370,23 +370,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bytea('hello world');
-INFO:  ('hello world', <type 'str'>)
+INFO:  (b'hello world', <class 'bytes'>)
  test_type_conversion_bytea 
 ----------------------------
  \x68656c6c6f20776f726c64
 (1 row)
 
 SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
-INFO:  ('null\x00byte', <type 'str'>)
+INFO:  (b'null\x00byte', <class 'bytes'>)
  test_type_conversion_bytea 
 ----------------------------
  \x6e756c6c0062797465
 (1 row)
 
 SELECT * FROM test_type_conversion_bytea(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_bytea 
 ----------------------------
  
@@ -395,14 +395,14 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
 import marshal
 return marshal.dumps('hello world')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
 import marshal
 try:
     return marshal.loads(x)
 except ValueError as e:
     return 'FAILED: ' + str(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
  test_type_unmarshal 
 ---------------------
@@ -415,7 +415,7 @@ SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
 CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
 CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_booltrue(true, true);
  test_type_conversion_booltrue 
 -------------------------------
@@ -432,21 +432,21 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
 CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_uint2 
 ----------------------------
                          50
 (1 row)
 
 SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
 ERROR:  value for domain uint2 violates check constraint "uint2_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_uint2"
 SELECT * FROM test_type_conversion_uint2(null, 1);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_uint2 
 ----------------------------
                           1
@@ -455,7 +455,7 @@ INFO:  (None, <type 'NoneType'>)
 CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
 CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_nnint(10, 20);
  test_type_conversion_nnint 
 ----------------------------
@@ -472,9 +472,9 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
 CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
-INFO:  ('hello wold', <type 'str'>)
+INFO:  (b'hello wold', <class 'bytes'>)
  test_type_conversion_bytea10 
 ------------------------------
  \x68656c6c6f20776f6c64
@@ -483,14 +483,14 @@ INFO:  ('hello wold', <type 'str'>)
 SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
-INFO:  ('hello word', <type 'str'>)
+INFO:  (b'hello word', <class 'bytes'>)
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_bytea10"
 SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 SELECT * FROM test_type_conversion_bytea10('hello word', null);
-INFO:  ('hello word', <type 'str'>)
+INFO:  (b'hello word', <class 'bytes'>)
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_bytea10"
@@ -500,58 +500,58 @@ PL/Python function "test_type_conversion_bytea10"
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
-INFO:  ([0, 100], <type 'list'>)
+INFO:  ([0, 100], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {0,100}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
-INFO:  ([0, -100, 55], <type 'list'>)
+INFO:  ([0, -100, 55], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {0,-100,55}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
-INFO:  ([None, 1], <type 'list'>)
+INFO:  ([None, 1], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {NULL,1}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
-INFO:  ([], <type 'list'>)
+INFO:  ([], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(NULL);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_array_int4 
 ---------------------------------
  
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-INFO:  ([[1, 2, 3], [4, 5, 6]], <type 'list'>)
+INFO:  ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {{1,2,3},{4,5,6}}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <type 'list'>)
+INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
           test_type_conversion_array_int4          
 ---------------------------------------------------
  {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
-INFO:  ([1, 2, 3], <type 'list'>)
+INFO:  ([1, 2, 3], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {1,2,3}
@@ -560,9 +560,9 @@ INFO:  ([1, 2, 3], <type 'list'>)
 CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
-INFO:  ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <type 'list'>)
+INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
           test_type_conversion_array_int8          
 ---------------------------------------------------
  {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
@@ -571,10 +571,10 @@ INFO:  ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <
 CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
             [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
-INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <type 'list'>)
+INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
                                                  test_type_conversion_array_date                                                 
 ---------------------------------------------------------------------------------------------------------------------------------
  {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
@@ -583,12 +583,12 @@ INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']
 CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
             [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
             [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
             ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
-INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <type 'list'>)
+INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
                                                                                                                                                       test_type_conversion_array_timestamp                                                                                                                                                      
 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
@@ -598,9 +598,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemint4(8,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
                                                                                                                                                                                                                                                                              pyreturnmultidemint4                                                                                                                                                                                                                                                                              
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
@@ -610,9 +610,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemint8(5,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
                                                                                                                                                                                    pyreturnmultidemint8                                                                                                                                                                                    
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
@@ -622,9 +622,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemfloat4(6,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
                                                                                                                                                                                                                 pyreturnmultidemfloat4                                                                                                                                                                                                                 
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
@@ -634,9 +634,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemfloat8(7,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
                                                                                                                                                                                                                                               pyreturnmultidemfloat8                                                                                                                                                                                                                                               
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
@@ -645,16 +645,16 @@ INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6],
 CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
-INFO:  (['foo', 'bar'], <type 'list'>)
+INFO:  (['foo', 'bar'], <class 'list'>)
  test_type_conversion_array_text 
 ---------------------------------
  {foo,bar}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
-INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
+INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
  test_type_conversion_array_text 
 ---------------------------------
  {{foo,bar},{foo2,bar2}}
@@ -663,9 +663,9 @@ INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
 CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
-INFO:  (['\xde\xad\xbe\xef', None], <type 'list'>)
+INFO:  ([b'\xde\xad\xbe\xef', None], <class 'list'>)
  test_type_conversion_array_bytea 
 ----------------------------------
  {"\\xdeadbeef",NULL}
@@ -673,7 +673,7 @@ INFO:  (['\xde\xad\xbe\xef', None], <type 'list'>)
 
 CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_mixed1();
  test_type_conversion_array_mixed1 
 -----------------------------------
@@ -682,14 +682,14 @@ SELECT * FROM test_type_conversion_array_mixed1();
 
 CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_mixed2();
 ERROR:  invalid input syntax for type integer: "abc"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_array_mixed2"
 CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
 return [[1,2,3],[4,5]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_mdarray_malformed();
 ERROR:  wrong length of inner sequence: has length 2, but 3 was expected
 DETAIL:  To construct a multidimensional array, the inner sequences must all have the same length.
@@ -697,14 +697,14 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_mdarray_malformed"
 CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
 return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_mdarray_toodeep();
 ERROR:  number of array dimensions exceeds the maximum allowed (6)
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_mdarray_toodeep"
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_record();
  test_type_conversion_array_record 
 -----------------------------------
@@ -713,7 +713,7 @@ SELECT * FROM test_type_conversion_array_record();
 
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_string();
  test_type_conversion_array_string 
 -----------------------------------
@@ -722,7 +722,7 @@ SELECT * FROM test_type_conversion_array_string();
 
 CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
 return ('abc', 'def')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_tuple();
  test_type_conversion_array_tuple 
 ----------------------------------
@@ -731,7 +731,7 @@ SELECT * FROM test_type_conversion_array_tuple();
 
 CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
 return 5
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_error();
 ERROR:  return value of function with array return type is not a Python sequence
 CONTEXT:  while creating return value
@@ -743,16 +743,16 @@ CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AN
 CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
-INFO:  ([0, 100], <type 'list'>)
+INFO:  ([0, 100], <class 'list'>)
  test_type_conversion_array_domain 
 -----------------------------------
  {0,100}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_array_domain 
 -----------------------------------
  
@@ -760,7 +760,7 @@ INFO:  (None, <type 'NoneType'>)
 
 CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
 return [2,1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain_check_violation();
 ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
 CONTEXT:  while creating return value
@@ -771,9 +771,9 @@ PL/Python function "test_type_conversion_array_domain_check_violation"
 CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_read_uint2_array(array[1::uint2]);
-INFO:  ([1], <type 'list'>)
+INFO:  ([1], <class 'list'>)
  test_read_uint2_array 
 -----------------------
                      1
@@ -781,7 +781,7 @@ INFO:  ([1], <type 'list'>)
 
 CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_build_uint2_array(1::int2);
  test_build_uint2_array 
 ------------------------
@@ -800,7 +800,7 @@ PL/Python function "test_build_uint2_array"
 CREATE FUNCTION test_type_conversion_domain_array(x integer[])
   RETURNS ordered_pair_domain[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_domain_array(array[2,4]);
 ERROR:  return value of function with array return type is not a Python sequence
 CONTEXT:  while creating return value
@@ -813,9 +813,9 @@ CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
   RETURNS integer AS $$
 plpy.info(x, type(x))
 return x[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_domain_array2(array[2,4]);
-INFO:  ([2, 4], <type 'list'>)
+INFO:  ([2, 4], <class 'list'>)
  test_type_conversion_domain_array2 
 ------------------------------------
                                   4
@@ -827,9 +827,9 @@ CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
   RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
-INFO:  ([[2, 4]], <type 'list'>)
+INFO:  ([[2, 4]], <class 'list'>)
  test_type_conversion_array_domain_array 
 -----------------------------------------
  {2,4}
@@ -846,7 +846,7 @@ CREATE TABLE employee (
 INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
 CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
 return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT name, test_composite_table_input(employee.*) FROM employee;
  name | test_composite_table_input 
 ------+----------------------------
@@ -876,7 +876,7 @@ CREATE TYPE named_pair AS (
 );
 CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
 return sum(p.values())
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_composite_type_input(row(1, 2));
  test_composite_type_input 
 ---------------------------
@@ -896,7 +896,7 @@ SELECT test_composite_type_input(row(1, 2));
 CREATE TYPE nnint_container AS (f1 int, f2 nnint);
 CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
 return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT nnint_test(null, 3);
  nnint_test 
 ------------
@@ -913,7 +913,7 @@ PL/Python function "nnint_test"
 CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
 CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
 return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT read_ordered_named_pair(row(1, 2));
  read_ordered_named_pair 
 -------------------------
@@ -924,7 +924,7 @@ SELECT read_ordered_named_pair(row(2, 1));  -- fail
 ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
 CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
 return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT build_ordered_named_pair(1,2);
  build_ordered_named_pair 
 --------------------------
@@ -937,7 +937,7 @@ CONTEXT:  while creating return value
 PL/Python function "build_ordered_named_pair"
 CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
 return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT build_ordered_named_pairs(1,2);
  build_ordered_named_pairs 
 ---------------------------
@@ -952,7 +952,7 @@ PL/Python function "build_ordered_named_pairs"
 -- Prepared statements
 --
 CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
 rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
@@ -965,7 +965,7 @@ SELECT test_prep_bool_input(); -- 1
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
 rv = plpy.execute(plan, [0], 5)
@@ -980,7 +980,7 @@ INFO:  {'val': False}
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
 rv = plpy.execute(plan, [bb], 5)
@@ -993,7 +993,7 @@ SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
 rv = plpy.execute(plan, [], 5)
@@ -1001,7 +1001,7 @@ plpy.info(rv[0])
 return rv[0]['val']
 $$;
 SELECT test_prep_bytea_output();
-INFO:  {'val': '\xaa\x00\xbb'}
+INFO:  {'val': b'\xaa\x00\xbb'}
  test_prep_bytea_output 
 ------------------------
  \xaa00bb
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
deleted file mode 100644
index a6ec10d5e18..00000000000
--- a/src/pl/plpython/expected/plpython_types_3.out
+++ /dev/null
@@ -1,1009 +0,0 @@
---
--- Test data type behavior
---
---
--- Base/common types
---
-CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bool(true);
-INFO:  (True, <class 'bool'>)
- test_type_conversion_bool 
----------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool(false);
-INFO:  (False, <class 'bool'>)
- test_type_conversion_bool 
----------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_bool 
----------------------------
- 
-(1 row)
-
--- test various other ways to express Booleans in Python
-CREATE FUNCTION test_type_conversion_bool_other(n int) RETURNS bool AS $$
-# numbers
-if n == 0:
-   ret = 0
-elif n == 1:
-   ret = 5
-# strings
-elif n == 2:
-   ret = ''
-elif n == 3:
-   ret = 'fa' # true in Python, false in PostgreSQL
-# containers
-elif n == 4:
-   ret = []
-elif n == 5:
-   ret = [0]
-plpy.info(ret, not not ret)
-return ret
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bool_other(0);
-INFO:  (0, False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(1);
-INFO:  (5, True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(2);
-INFO:  ('', False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(3);
-INFO:  ('fa', True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(4);
-INFO:  ([], False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(5);
-INFO:  ([0], True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_char('a');
-INFO:  ('a', <class 'str'>)
- test_type_conversion_char 
----------------------------
- a
-(1 row)
-
-SELECT * FROM test_type_conversion_char(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_char 
----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int2(100::int2);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int2 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int2(-100::int2);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int2 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int2(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int2 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int4(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int4 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int4(-100);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int4 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int4(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int4 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int8(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(-100);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(5000000000);
-INFO:  (5000000000, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                5000000000
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int8 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
-# print just the class name, not the type, to avoid differences
-# between decimal and cdecimal
-plpy.info(str(x), x.__class__.__name__)
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_numeric(100);
-INFO:  ('100', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                          100
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(-100);
-INFO:  ('-100', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                         -100
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(100.0);
-INFO:  ('100.0', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                        100.0
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(100.00);
-INFO:  ('100.00', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                       100.00
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(5000000000.5);
-INFO:  ('5000000000.5', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                 5000000000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(1234567890.0987654321);
-INFO:  ('1234567890.0987654321', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-        1234567890.0987654321
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(-1234567890.0987654321);
-INFO:  ('-1234567890.0987654321', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-       -1234567890.0987654321
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(null);
-INFO:  ('None', 'NoneType')
- test_type_conversion_numeric 
-------------------------------
-                             
-(1 row)
-
-CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_float4(100);
-INFO:  (100.0, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                         100
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(-100);
-INFO:  (-100.0, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                        -100
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(5000.5);
-INFO:  (5000.5, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                      5000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_float4 
------------------------------
-                            
-(1 row)
-
-CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_float8(100);
-INFO:  (100.0, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                         100
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(-100);
-INFO:  (-100.0, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                        -100
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(5000000000.5);
-INFO:  (5000000000.5, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                5000000000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_float8 
------------------------------
-                            
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(100100100.654321);
-INFO:  (100100100.654321, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-            100100100.654321
-(1 row)
-
-CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_oid(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_oid 
---------------------------
-                      100
-(1 row)
-
-SELECT * FROM test_type_conversion_oid(2147483649);
-INFO:  (2147483649, <class 'int'>)
- test_type_conversion_oid 
---------------------------
-               2147483649
-(1 row)
-
-SELECT * FROM test_type_conversion_oid(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_oid 
---------------------------
-                         
-(1 row)
-
-CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_text('hello world');
-INFO:  ('hello world', <class 'str'>)
- test_type_conversion_text 
----------------------------
- hello world
-(1 row)
-
-SELECT * FROM test_type_conversion_text(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_text 
----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bytea('hello world');
-INFO:  (b'hello world', <class 'bytes'>)
- test_type_conversion_bytea 
-----------------------------
- \x68656c6c6f20776f726c64
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
-INFO:  (b'null\x00byte', <class 'bytes'>)
- test_type_conversion_bytea 
-----------------------------
- \x6e756c6c0062797465
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_bytea 
-----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
-import marshal
-return marshal.dumps('hello world')
-$$ LANGUAGE plpython3u;
-CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
-import marshal
-try:
-    return marshal.loads(x)
-except ValueError as e:
-    return 'FAILED: ' + str(e)
-$$ LANGUAGE plpython3u;
-SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
- test_type_unmarshal 
----------------------
- hello world
-(1 row)
-
---
--- Domains
---
-CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
-CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_booltrue(true, true);
- test_type_conversion_booltrue 
--------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_booltrue(false, true);
-ERROR:  value for domain booltrue violates check constraint "booltrue_check"
-SELECT * FROM test_type_conversion_booltrue(true, false);
-ERROR:  value for domain booltrue violates check constraint "booltrue_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_booltrue"
-CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
-CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
-plpy.info(x, type(x))
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
-INFO:  (100, <class 'int'>)
- test_type_conversion_uint2 
-----------------------------
-                         50
-(1 row)
-
-SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
-INFO:  (100, <class 'int'>)
-ERROR:  value for domain uint2 violates check constraint "uint2_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_uint2"
-SELECT * FROM test_type_conversion_uint2(null, 1);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_uint2 
-----------------------------
-                          1
-(1 row)
-
-CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
-CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_nnint(10, 20);
- test_type_conversion_nnint 
-----------------------------
-                         20
-(1 row)
-
-SELECT * FROM test_type_conversion_nnint(null, 20);
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-SELECT * FROM test_type_conversion_nnint(10, null);
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_nnint"
-CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL);
-CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
-plpy.info(x, type(x))
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
-INFO:  (b'hello wold', <class 'bytes'>)
- test_type_conversion_bytea10 
-------------------------------
- \x68656c6c6f20776f6c64
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
-INFO:  (b'hello word', <class 'bytes'>)
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_bytea10"
-SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-SELECT * FROM test_type_conversion_bytea10('hello word', null);
-INFO:  (b'hello word', <class 'bytes'>)
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_bytea10"
---
--- Arrays
---
-CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
-INFO:  ([0, 100], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {0,100}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
-INFO:  ([0, -100, 55], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {0,-100,55}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
-INFO:  ([None, 1], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {NULL,1}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
-INFO:  ([], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(NULL);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_array_int4 
----------------------------------
- 
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-INFO:  ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {{1,2,3},{4,5,6}}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
-          test_type_conversion_array_int4          
----------------------------------------------------
- {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
-INFO:  ([1, 2, 3], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {1,2,3}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
-          test_type_conversion_array_int8          
----------------------------------------------------
- {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
-            [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
-INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
-                                                 test_type_conversion_array_date                                                 
----------------------------------------------------------------------------------------------------------------------------------
- {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
-            [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
-            [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
-            ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
-INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
-                                                                                                                                                      test_type_conversion_array_timestamp                                                                                                                                                      
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 ) RETURNS int4[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemint4(8,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
-                                                                                                                                                                                                                                                                             pyreturnmultidemint4                                                                                                                                                                                                                                                                              
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 ) RETURNS int8[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemint8(5,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
-                                                                                                                                                                                   pyreturnmultidemint8                                                                                                                                                                                    
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4 ) RETURNS float4[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemfloat4(6,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
-                                                                                                                                                                                                                pyreturnmultidemfloat4                                                                                                                                                                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4 ) RETURNS float8[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemfloat8(7,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
-                                                                                                                                                                                                                                              pyreturnmultidemfloat8                                                                                                                                                                                                                                               
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
-INFO:  (['foo', 'bar'], <class 'list'>)
- test_type_conversion_array_text 
----------------------------------
- {foo,bar}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
-INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
- test_type_conversion_array_text 
----------------------------------
- {{foo,bar},{foo2,bar2}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
-INFO:  ([b'\xde\xad\xbe\xef', None], <class 'list'>)
- test_type_conversion_array_bytea 
-----------------------------------
- {"\\xdeadbeef",NULL}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
-return [123, 'abc']
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_mixed1();
- test_type_conversion_array_mixed1 
------------------------------------
- {123,abc}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
-return [123, 'abc']
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_mixed2();
-ERROR:  invalid input syntax for type integer: "abc"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_mixed2"
-CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
-return [[1,2,3],[4,5]]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_mdarray_malformed();
-ERROR:  wrong length of inner sequence: has length 2, but 3 was expected
-DETAIL:  To construct a multidimensional array, the inner sequences must all have the same length.
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_mdarray_malformed"
-CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
-return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_mdarray_toodeep();
-ERROR:  number of array dimensions exceeds the maximum allowed (6)
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_mdarray_toodeep"
-CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
-return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_record();
- test_type_conversion_array_record 
------------------------------------
- {"(one,42)","(two,11)"}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
-return 'abc'
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_string();
- test_type_conversion_array_string 
------------------------------------
- {a,b,c}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
-return ('abc', 'def')
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_tuple();
- test_type_conversion_array_tuple 
-----------------------------------
- {abc,def}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
-return 5
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_error();
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_error"
---
--- Domains over arrays
---
-CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AND VALUE[1] < VALUE[2]);
-CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
-INFO:  ([0, 100], <class 'list'>)
- test_type_conversion_array_domain 
------------------------------------
- {0,100}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_array_domain 
------------------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
-return [2,1]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_domain_check_violation();
-ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_domain_check_violation"
---
--- Arrays of domains
---
-CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
-plpy.info(x, type(x))
-return x[0]
-$$ LANGUAGE plpythonu;
-select test_read_uint2_array(array[1::uint2]);
-INFO:  ([1], <class 'list'>)
- test_read_uint2_array 
------------------------
-                     1
-(1 row)
-
-CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
-return [x, x]
-$$ LANGUAGE plpythonu;
-select test_build_uint2_array(1::int2);
- test_build_uint2_array 
-------------------------
- {1,1}
-(1 row)
-
-select test_build_uint2_array(-1::int2);  -- fail
-ERROR:  value for domain uint2 violates check constraint "uint2_check"
-CONTEXT:  while creating return value
-PL/Python function "test_build_uint2_array"
---
--- ideally this would work, but for now it doesn't, because the return value
--- is [[2,4], [2,4]] which our conversion code thinks should become a 2-D
--- integer array, not an array of arrays.
---
-CREATE FUNCTION test_type_conversion_domain_array(x integer[])
-  RETURNS ordered_pair_domain[] AS $$
-return [x, x]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_domain_array(array[2,4]);
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_domain_array"
-select test_type_conversion_domain_array(array[4,2]);  -- fail
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_domain_array"
-CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
-  RETURNS integer AS $$
-plpy.info(x, type(x))
-return x[1]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_domain_array2(array[2,4]);
-INFO:  ([2, 4], <class 'list'>)
- test_type_conversion_domain_array2 
-------------------------------------
-                                  4
-(1 row)
-
-select test_type_conversion_domain_array2(array[4,2]);  -- fail
-ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
-CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
-  RETURNS ordered_pair_domain AS $$
-plpy.info(x, type(x))
-return x[0]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
-INFO:  ([[2, 4]], <class 'list'>)
- test_type_conversion_array_domain_array 
------------------------------------------
- {2,4}
-(1 row)
-
----
---- Composite types
----
-CREATE TABLE employee (
-    name text,
-    basesalary integer,
-    bonus integer
-);
-INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
-CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
-return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpython3u;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
- name | test_composite_table_input 
-------+----------------------------
- John |                        110
- Mary |                        210
-(2 rows)
-
-ALTER TABLE employee DROP bonus;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
-ERROR:  KeyError: 'bonus'
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "test_composite_table_input", line 2, in <module>
-    return e['basesalary'] + e['bonus']
-PL/Python function "test_composite_table_input"
-ALTER TABLE employee ADD bonus integer;
-UPDATE employee SET bonus = 10;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
- name | test_composite_table_input 
-------+----------------------------
- John |                        110
- Mary |                        210
-(2 rows)
-
-CREATE TYPE named_pair AS (
-    i integer,
-    j integer
-);
-CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
-return sum(p.values())
-$$ LANGUAGE plpython3u;
-SELECT test_composite_type_input(row(1, 2));
- test_composite_type_input 
----------------------------
-                         3
-(1 row)
-
-ALTER TYPE named_pair RENAME TO named_pair_2;
-SELECT test_composite_type_input(row(1, 2));
- test_composite_type_input 
----------------------------
-                         3
-(1 row)
-
---
--- Domains within composite
---
-CREATE TYPE nnint_container AS (f1 int, f2 nnint);
-CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
-return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
-SELECT nnint_test(null, 3);
- nnint_test 
-------------
- (,3)
-(1 row)
-
-SELECT nnint_test(3, null);  -- fail
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-CONTEXT:  while creating return value
-PL/Python function "nnint_test"
---
--- Domains of composite
---
-CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
-CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
-return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
-SELECT read_ordered_named_pair(row(1, 2));
- read_ordered_named_pair 
--------------------------
-                       3
-(1 row)
-
-SELECT read_ordered_named_pair(row(2, 1));  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
-return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
-SELECT build_ordered_named_pair(1,2);
- build_ordered_named_pair 
---------------------------
- (1,2)
-(1 row)
-
-SELECT build_ordered_named_pair(2,1);  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CONTEXT:  while creating return value
-PL/Python function "build_ordered_named_pair"
-CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
-return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
-SELECT build_ordered_named_pairs(1,2);
- build_ordered_named_pairs 
----------------------------
- {"(1,2)","(1,3)"}
-(1 row)
-
-SELECT build_ordered_named_pairs(2,1);  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CONTEXT:  while creating return value
-PL/Python function "build_ordered_named_pairs"
---
--- Prepared statements
---
-CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
-rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
-return rv[0]['val']
-$$;
-SELECT test_prep_bool_input(); -- 1
- test_prep_bool_input 
-----------------------
-                    1
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
-rv = plpy.execute(plan, [0], 5)
-plpy.info(rv[0])
-return rv[0]['val']
-$$;
-SELECT test_prep_bool_output(); -- false
-INFO:  {'val': False}
- test_prep_bool_output 
------------------------
- f
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
-rv = plpy.execute(plan, [bb], 5)
-return rv[0]['val']
-$$;
-SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
- test_prep_bytea_input 
------------------------
-                     3
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
-rv = plpy.execute(plan, [], 5)
-plpy.info(rv[0])
-return rv[0]['val']
-$$;
-SELECT test_prep_bytea_output();
-INFO:  {'val': b'\xaa\x00\xbb'}
- test_prep_bytea_output 
-------------------------
- \xaa00bb
-(1 row)
-
diff --git a/src/pl/plpython/expected/plpython_unicode.out b/src/pl/plpython/expected/plpython_unicode.out
index c7546dd4587..fd54b0b88e8 100644
--- a/src/pl/plpython/expected/plpython_unicode.out
+++ b/src/pl/plpython/expected/plpython_unicode.out
@@ -11,24 +11,24 @@ CREATE TABLE unicode_test (
 	testvalue  text NOT NULL
 );
 CREATE FUNCTION unicode_return() RETURNS text AS E'
-return u"\\xA0"
-' LANGUAGE plpythonu;
+return "\\xA0"
+' LANGUAGE plpython3u;
 CREATE FUNCTION unicode_trigger() RETURNS trigger AS E'
-TD["new"]["testvalue"] = u"\\xA0"
+TD["new"]["testvalue"] = "\\xA0"
 return "MODIFY"
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test
   FOR EACH ROW EXECUTE PROCEDURE unicode_trigger();
 CREATE FUNCTION unicode_plan1() RETURNS text AS E'
 plan = plpy.prepare("SELECT $1 AS testvalue", ["text"])
-rv = plpy.execute(plan, [u"\\xA0"], 1)
+rv = plpy.execute(plan, ["\\xA0"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 CREATE FUNCTION unicode_plan2() RETURNS text AS E'
-plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"])
+plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"])
 rv = plpy.execute(plan, ["foo", "bar"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 SELECT unicode_return();
  unicode_return 
 ----------------
diff --git a/src/pl/plpython/expected/plpython_void.out b/src/pl/plpython/expected/plpython_void.out
index 1080d12d6b2..07d0760783e 100644
--- a/src/pl/plpython/expected/plpython_void.out
+++ b/src/pl/plpython/expected/plpython_void.out
@@ -3,14 +3,14 @@
 --
 CREATE FUNCTION test_void_func1() RETURNS void AS $$
 x = 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- illegal: can't return non-None value in void-returning func
 CREATE FUNCTION test_void_func2() RETURNS void AS $$
 return 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_return_none() RETURNS int AS $$
 None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- Tests for functions returning void
 SELECT test_void_func1(), test_void_func1() IS NULL AS "is null";
  test_void_func1 | is null 
diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index 08d8b607e38..f8591358bc8 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -40,7 +40,7 @@ static PyTypeObject PLy_CursorType = {
 	.tp_name = "PLyCursor",
 	.tp_basicsize = sizeof(PLyCursorObject),
 	.tp_dealloc = PLy_cursor_dealloc,
-	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER,
+	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
 	.tp_doc = PLy_cursor_doc,
 	.tp_iter = PyObject_SelfIter,
 	.tp_iternext = PLy_cursor_iternext,
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index 3eedaa80da7..21303c1586b 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -28,27 +28,13 @@
  * exported functions
  */
 
-#if PY_MAJOR_VERSION >= 3
-/* Use separate names to reduce confusion */
-#define plpython_validator plpython3_validator
-#define plpython_call_handler plpython3_call_handler
-#define plpython_inline_handler plpython3_inline_handler
-#endif
-
 extern void _PG_init(void);
 
 PG_MODULE_MAGIC;
 
-PG_FUNCTION_INFO_V1(plpython_validator);
-PG_FUNCTION_INFO_V1(plpython_call_handler);
-PG_FUNCTION_INFO_V1(plpython_inline_handler);
-
-#if PY_MAJOR_VERSION < 3
-/* Define aliases plpython2_call_handler etc */
-PG_FUNCTION_INFO_V1(plpython2_validator);
-PG_FUNCTION_INFO_V1(plpython2_call_handler);
-PG_FUNCTION_INFO_V1(plpython2_inline_handler);
-#endif
+PG_FUNCTION_INFO_V1(plpython3_validator);
+PG_FUNCTION_INFO_V1(plpython3_call_handler);
+PG_FUNCTION_INFO_V1(plpython3_inline_handler);
 
 
 static bool PLy_procedure_is_trigger(Form_pg_proc procStruct);
@@ -125,13 +111,9 @@ PLy_initialize(void)
 	if (inited)
 		return;
 
-#if PY_MAJOR_VERSION >= 3
 	PyImport_AppendInittab("plpy", PyInit_plpy);
-#endif
 	Py_Initialize();
-#if PY_MAJOR_VERSION >= 3
 	PyImport_ImportModule("plpy");
-#endif
 	PLy_init_interp();
 	PLy_init_plpy();
 	if (PyErr_Occurred())
@@ -171,7 +153,7 @@ PLy_init_interp(void)
 }
 
 Datum
-plpython_validator(PG_FUNCTION_ARGS)
+plpython3_validator(PG_FUNCTION_ARGS)
 {
 	Oid			funcoid = PG_GETARG_OID(0);
 	HeapTuple	tuple;
@@ -203,17 +185,8 @@ plpython_validator(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-#if PY_MAJOR_VERSION < 3
 Datum
-plpython2_validator(PG_FUNCTION_ARGS)
-{
-	/* call plpython validator with our fcinfo so it gets our oid */
-	return plpython_validator(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
-Datum
-plpython_call_handler(PG_FUNCTION_ARGS)
+plpython3_call_handler(PG_FUNCTION_ARGS)
 {
 	bool		nonatomic;
 	Datum		retval;
@@ -284,16 +257,8 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 	return retval;
 }
 
-#if PY_MAJOR_VERSION < 3
 Datum
-plpython2_call_handler(PG_FUNCTION_ARGS)
-{
-	return plpython_call_handler(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
-Datum
-plpython_inline_handler(PG_FUNCTION_ARGS)
+plpython3_inline_handler(PG_FUNCTION_ARGS)
 {
 	LOCAL_FCINFO(fake_fcinfo, 0);
 	InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
@@ -368,14 +333,6 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-#if PY_MAJOR_VERSION < 3
-Datum
-plpython2_inline_handler(PG_FUNCTION_ARGS)
-{
-	return plpython_inline_handler(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
 static bool
 PLy_procedure_is_trigger(Form_pg_proc procStruct)
 {
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
index 0365acc95b0..0eefd34afcd 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -109,7 +109,6 @@ static PyMethodDef PLy_exc_methods[] = {
 	{NULL, NULL, 0, NULL}
 };
 
-#if PY_MAJOR_VERSION >= 3
 static PyModuleDef PLy_module = {
 	PyModuleDef_HEAD_INIT,
 	.m_name = "plpy",
@@ -141,7 +140,6 @@ PyInit_plpy(void)
 
 	return m;
 }
-#endif							/* PY_MAJOR_VERSION >= 3 */
 
 void
 PLy_init_plpy(void)
@@ -150,10 +148,6 @@ PLy_init_plpy(void)
 			   *main_dict,
 			   *plpy_mod;
 
-#if PY_MAJOR_VERSION < 3
-	PyObject   *plpy;
-#endif
-
 	/*
 	 * initialize plpy module
 	 */
@@ -162,13 +156,7 @@ PLy_init_plpy(void)
 	PLy_subtransaction_init_type();
 	PLy_cursor_init_type();
 
-#if PY_MAJOR_VERSION >= 3
 	PyModule_Create(&PLy_module);
-	/* for Python 3 we initialized the exceptions in PyInit_plpy */
-#else
-	plpy = Py_InitModule("plpy", PLy_methods);
-	PLy_add_exceptions(plpy);
-#endif
 
 	/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
 
@@ -191,11 +179,7 @@ PLy_add_exceptions(PyObject *plpy)
 	PyObject   *excmod;
 	HASHCTL		hash_ctl;
 
-#if PY_MAJOR_VERSION < 3
-	excmod = Py_InitModule("spiexceptions", PLy_exc_methods);
-#else
 	excmod = PyModule_Create(&PLy_exc_module);
-#endif
 	if (excmod == NULL)
 		PLy_elog(ERROR, "could not create the spiexceptions module");
 
diff --git a/src/pl/plpython/plpy_plpymodule.h b/src/pl/plpython/plpy_plpymodule.h
index 54d78101ceb..ad6436aca78 100644
--- a/src/pl/plpython/plpy_plpymodule.h
+++ b/src/pl/plpython/plpy_plpymodule.h
@@ -11,9 +11,7 @@
 extern HTAB *PLy_spi_exceptions;
 
 
-#if PY_MAJOR_VERSION >= 3
 PyMODINIT_FUNC PyInit_plpy(void);
-#endif
 extern void PLy_init_plpy(void);
 
 #endif							/* PLPY_PLPYMODULE_H */
diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c
index 54f39419c84..f289617ba80 100644
--- a/src/pl/plpython/plpy_resultobject.c
+++ b/src/pl/plpython/plpy_resultobject.c
@@ -226,19 +226,11 @@ PLy_result_str(PyObject *arg)
 {
 	PLyResultObject *ob = (PLyResultObject *) arg;
 
-#if PY_MAJOR_VERSION >= 3
 	return PyUnicode_FromFormat("<%s status=%S nrows=%S rows=%S>",
 								Py_TYPE(ob)->tp_name,
 								ob->status,
 								ob->nrows,
 								ob->rows);
-#else
-	return PyString_FromFormat("<%s status=%ld nrows=%ld rows=%s>",
-							   ob->ob_type->tp_name,
-							   PyInt_AsLong(ob->status),
-							   PyInt_AsLong(ob->nrows),
-							   PyString_AsString(PyObject_Str(ob->rows)));
-#endif
 }
 
 static PyObject *
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 5e807b139f1..adf37a9b882 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -1032,25 +1032,17 @@ PLyObject_AsString(PyObject *plrv)
 	else if (PyFloat_Check(plrv))
 	{
 		/* use repr() for floats, str() is lossy */
-#if PY_MAJOR_VERSION >= 3
 		PyObject   *s = PyObject_Repr(plrv);
 
 		plrv_bo = PLyUnicode_Bytes(s);
 		Py_XDECREF(s);
-#else
-		plrv_bo = PyObject_Repr(plrv);
-#endif
 	}
 	else
 	{
-#if PY_MAJOR_VERSION >= 3
 		PyObject   *s = PyObject_Str(plrv);
 
 		plrv_bo = PLyUnicode_Bytes(s);
 		Py_XDECREF(s);
-#else
-		plrv_bo = PyObject_Str(plrv);
-#endif
 	}
 	if (!plrv_bo)
 		PLy_elog(ERROR, "could not create string representation of Python object");
diff --git a/src/pl/plpython/plpy_util.c b/src/pl/plpython/plpy_util.c
index 4a7d7264d79..693d0396c48 100644
--- a/src/pl/plpython/plpy_util.c
+++ b/src/pl/plpython/plpy_util.c
@@ -95,7 +95,6 @@ PLyUnicode_AsString(PyObject *unicode)
 	return rv;
 }
 
-#if PY_MAJOR_VERSION >= 3
 /*
  * Convert a C string in the PostgreSQL server encoding to a Python
  * unicode object.  Reference ownership is passed to the caller.
@@ -126,5 +125,3 @@ PLyUnicode_FromString(const char *s)
 {
 	return PLyUnicode_FromStringAndSize(s, strlen(s));
 }
-
-#endif							/* PY_MAJOR_VERSION >= 3 */
diff --git a/src/pl/plpython/plpy_util.h b/src/pl/plpython/plpy_util.h
index c9ba7edc0ec..7c6577925ea 100644
--- a/src/pl/plpython/plpy_util.h
+++ b/src/pl/plpython/plpy_util.h
@@ -11,9 +11,7 @@
 extern PyObject *PLyUnicode_Bytes(PyObject *unicode);
 extern char *PLyUnicode_AsString(PyObject *unicode);
 
-#if PY_MAJOR_VERSION >= 3
 extern PyObject *PLyUnicode_FromString(const char *s);
 extern PyObject *PLyUnicode_FromStringAndSize(const char *s, Py_ssize_t size);
-#endif
 
 #endif							/* PLPY_UTIL_H */
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 05e4362dab9..798c3707301 100644
--- a/src/pl/plpython/plpython.h
+++ b/src/pl/plpython/plpython.h
@@ -69,26 +69,21 @@
  * compatibility layer for Python 3 that when asked to convert a C
  * string to a Python string it converts the C string from the
  * PostgreSQL server encoding to a Python Unicode object.
+ *
+ * FIXME
  */
-#if PY_MAJOR_VERSION >= 3
 #define PyString_Check(x) 0
 #define PyString_AsString(x) PLyUnicode_AsString(x)
 #define PyString_FromString(x) PLyUnicode_FromString(x)
 #define PyString_FromStringAndSize(x, size) PLyUnicode_FromStringAndSize(x, size)
-#endif
 
 /*
  * Python 3 only has long.
+ *
+ * FIXME
  */
-#if PY_MAJOR_VERSION >= 3
 #define PyInt_FromLong(x) PyLong_FromLong(x)
 #define PyInt_AsLong(x) PyLong_AsLong(x)
-#endif
-
-/* Python 3 removed the Py_TPFLAGS_HAVE_ITER flag */
-#if PY_MAJOR_VERSION >= 3
-#define Py_TPFLAGS_HAVE_ITER 0
-#endif
 
 /* define our text domain for translations */
 #undef TEXTDOMAIN
diff --git a/src/pl/plpython/plpython2u--1.0.sql b/src/pl/plpython/plpython2u--1.0.sql
deleted file mode 100644
index 69f74775678..00000000000
--- a/src/pl/plpython/plpython2u--1.0.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/* src/pl/plpython/plpython2u--1.0.sql */
-
-CREATE FUNCTION plpython2_call_handler() RETURNS language_handler
-  LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython2_inline_handler(internal) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython2_validator(oid) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE LANGUAGE plpython2u
-  HANDLER plpython2_call_handler
-  INLINE plpython2_inline_handler
-  VALIDATOR plpython2_validator;
-
-COMMENT ON LANGUAGE plpython2u IS 'PL/Python2U untrusted procedural language';
diff --git a/src/pl/plpython/plpython2u.control b/src/pl/plpython/plpython2u.control
deleted file mode 100644
index 39c2b791efe..00000000000
--- a/src/pl/plpython/plpython2u.control
+++ /dev/null
@@ -1,7 +0,0 @@
-# plpython2u extension
-comment = 'PL/Python2U untrusted procedural language'
-default_version = '1.0'
-module_pathname = '$libdir/plpython2'
-relocatable = false
-schema = pg_catalog
-superuser = true
diff --git a/src/pl/plpython/plpythonu--1.0.sql b/src/pl/plpython/plpythonu--1.0.sql
deleted file mode 100644
index 4c6f7c3f140..00000000000
--- a/src/pl/plpython/plpythonu--1.0.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/* src/pl/plpython/plpythonu--1.0.sql */
-
-CREATE FUNCTION plpython_call_handler() RETURNS language_handler
-  LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_inline_handler(internal) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_validator(oid) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE LANGUAGE plpythonu
-  HANDLER plpython_call_handler
-  INLINE plpython_inline_handler
-  VALIDATOR plpython_validator;
-
-COMMENT ON LANGUAGE plpythonu IS 'PL/PythonU untrusted procedural language';
diff --git a/src/pl/plpython/plpythonu.control b/src/pl/plpython/plpythonu.control
deleted file mode 100644
index ae91b1c255c..00000000000
--- a/src/pl/plpython/plpythonu.control
+++ /dev/null
@@ -1,7 +0,0 @@
-# plpythonu extension
-comment = 'PL/PythonU untrusted procedural language'
-default_version = '1.0'
-module_pathname = '$libdir/plpython2'
-relocatable = false
-schema = pg_catalog
-superuser = true
diff --git a/src/pl/plpython/regress-python3-mangle.mk b/src/pl/plpython/regress-python3-mangle.mk
deleted file mode 100644
index a785818a172..00000000000
--- a/src/pl/plpython/regress-python3-mangle.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-ifeq ($(python_majorversion),3)
-# Adjust regression tests for Python 3 compatibility
-#
-# Mention those regression test files that need to be mangled in the
-# variable REGRESS_PLPYTHON3_MANGLE.  They will be copied to a
-# subdirectory python3/ and have their Python syntax and other bits
-# adjusted to work with Python 3.
-
-# Note that the order of the tests needs to be preserved in this
-# expression.
-REGRESS := $(foreach test,$(REGRESS),$(if $(filter $(test),$(REGRESS_PLPYTHON3_MANGLE)),python3/$(test),$(test)))
-
-.PHONY: pgregress-python3-mangle
-pgregress-python3-mangle:
-	$(MKDIR_P) sql/python3 expected/python3 results/python3
-	for file in $(patsubst %,$(srcdir)/sql/%.sql,$(REGRESS_PLPYTHON3_MANGLE)) $(patsubst %,$(srcdir)/expected/%*.out,$(REGRESS_PLPYTHON3_MANGLE)); do \
-	  sed \
-	      -e "s/<type 'exceptions\.\([[:alpha:]]*\)'>/<class '\1'>/g" \
-	      -e "s/<type 'long'>/<class 'int'>/g" \
-	      -e "s/\([0-9][0-9]*\)L/\1/g" \
-	      -e 's/\([ [{]\)u"/\1"/g' \
-	      -e "s/\([ [{]\)u'/\1'/g" \
-	      -e "s/def next/def __next__/g" \
-	      -e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
-	      -e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
-	      -e "s/EXTENSION plpythonu/EXTENSION plpython3u/g" \
-	      -e "s/EXTENSION plpython2u/EXTENSION plpython3u/g" \
-	      -e "s/EXTENSION \([^ ]*\)_plpythonu/EXTENSION \1_plpython3u/g" \
-	      -e "s/EXTENSION \([^ ]*\)_plpython2u/EXTENSION \1_plpython3u/g" \
-	      -e 's/installing required extension "plpython2u"/installing required extension "plpython3u"/g' \
-	    $$file >`echo $$file | sed 's,^.*/\([^/][^/]*/\)\([^/][^/]*\)$$,\1python3/\2,'` || exit; \
-	done
-
-check installcheck: pgregress-python3-mangle
-
-pg_regress_clean_files += sql/python3/ expected/python3/ results/python3/
-
-endif # Python 3
diff --git a/src/pl/plpython/sql/plpython_call.sql b/src/pl/plpython/sql/plpython_call.sql
index b0b3705ae3c..daa4bc377d7 100644
--- a/src/pl/plpython/sql/plpython_call.sql
+++ b/src/pl/plpython/sql/plpython_call.sql
@@ -3,7 +3,7 @@
 --
 
 CREATE PROCEDURE test_proc1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 pass
 $$;
@@ -13,7 +13,7 @@ CALL test_proc1();
 
 -- error: can't return non-None
 CREATE PROCEDURE test_proc2()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return 5
 $$;
@@ -24,7 +24,7 @@ CALL test_proc2();
 CREATE TABLE test1 (a int);
 
 CREATE PROCEDURE test_proc3(x int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("INSERT INTO test1 VALUES (%s)" % x)
 $$;
@@ -37,7 +37,7 @@ SELECT * FROM test1;
 -- output arguments
 
 CREATE PROCEDURE test_proc5(INOUT a text)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return [a + '+' + a]
 $$;
@@ -46,7 +46,7 @@ CALL test_proc5('abc');
 
 
 CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return (b * a, c * a)
 $$;
@@ -57,7 +57,7 @@ CALL test_proc6(2, 3, 4);
 -- OUT parameters
 
 CREATE PROCEDURE test_proc9(IN a int, OUT b int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.notice("a: %s" % (a))
 return (a * 2,)
diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql
index 0fd2f5d5e3b..21757701cc1 100644
--- a/src/pl/plpython/sql/plpython_composite.sql
+++ b/src/pl/plpython/sql/plpython_composite.sql
@@ -1,6 +1,6 @@
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT multiout_simple();
 SELECT * FROM multiout_simple();
@@ -9,7 +9,7 @@ SELECT (multiout_simple()).j + 3;
 
 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT multiout_simple_setof();
 SELECT * FROM multiout_simple_setof();
@@ -34,7 +34,7 @@ elif typ == 'obj':
     return type_record
 elif typ == 'str':
     return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
 SELECT multiout_record_as('dict', 'foo', 1, 'f');
@@ -77,7 +77,7 @@ for i in range(n):
     power = 2 ** i
     length = plpy.execute("select length('%d')" % power)[0]['length']
     yield power, length
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_setof(3);
 SELECT multiout_setof(5);
@@ -86,7 +86,7 @@ CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
 return [{'x': 4, 'y' :'four'},
         {'x': 7, 'y' :'seven'},
         {'x': 0, 'y' :'zero'}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_return_table();
 
@@ -94,18 +94,18 @@ CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS
 yield [[1], 'a']
 yield [[1,2], 'b']
 yield [[1,2,3], None]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_array();
 
 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
 return {'first': 1, 'second': 2}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
 return [{'first': 1, 'second': 2},
        {'first': 3, 'second': 4	}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM singleout_composite();
 SELECT * FROM multiout_composite();
@@ -113,7 +113,7 @@ SELECT * FROM multiout_composite();
 -- composite OUT parameters in functions returning RECORD not supported yet
 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
 return (n, (n * 2, n * 3))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
 if returnnull:
@@ -132,7 +132,7 @@ elif typ == 'str':
     d = "(%r,%r)" % (n * 2, n * 3)
 for i in range(n):
     yield (i, d)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_composite(2);
 SELECT * FROM multiout_table_type_setof('dict', 'f', 3);
@@ -157,7 +157,7 @@ CREATE TABLE changing (
 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
 return [(1, {'i': 1, 'j': 2}),
         (1, (3, 4))]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM changing_test();
 ALTER TABLE changing DROP COLUMN j;
@@ -178,14 +178,14 @@ yield {'tab': [('first', 1), ('second', 2)],
 yield {'tab': [('first', 1), ('second', 2)],
       'typ': [{'first': 'third', 'second': 3},
               {'first': 'fourth', 'second': 4}]}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM composite_types_table();
 
 -- check what happens if the output record descriptor changes
 CREATE FUNCTION return_record(t text) RETURNS record AS $$
 return {'t': t, 'val': 10}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM return_record('abc') AS r(t text, val integer);
 SELECT * FROM return_record('abc') AS r(t text, val bigint);
@@ -196,7 +196,7 @@ SELECT * FROM return_record('999') AS r(val text, t integer);
 
 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
 return {'v1':1,'v2':2,t:3}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
 SELECT * FROM return_record_2('v3') AS (v2 int, v3 int, v1 int);
@@ -211,7 +211,7 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
 -- multi-dimensional array of composite types.
 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list();
 
 -- Starting with PostgreSQL 10, a composite type in an array cannot be
@@ -220,5 +220,5 @@ SELECT * FROM composite_type_as_list();
 -- on the issue.
 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
   return [['first', 1]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list_broken();
diff --git a/src/pl/plpython/sql/plpython_do.sql b/src/pl/plpython/sql/plpython_do.sql
index 0e281a08ee8..d49413268e9 100644
--- a/src/pl/plpython/sql/plpython_do.sql
+++ b/src/pl/plpython/sql/plpython_do.sql
@@ -1,5 +1,3 @@
-DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
+DO $$ plpy.notice("This is plpython3u.") $$ LANGUAGE plpython3u;
 
-DO $$ plpy.notice("This is plpython2u.") $$ LANGUAGE plpython2u;
-
-DO $$ raise Exception("error test") $$ LANGUAGE plpythonu;
+DO $$ raise Exception("error test") $$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/sql/plpython_drop.sql b/src/pl/plpython/sql/plpython_drop.sql
index 72d5d657ec3..e4f373b2bc7 100644
--- a/src/pl/plpython/sql/plpython_drop.sql
+++ b/src/pl/plpython/sql/plpython_drop.sql
@@ -3,6 +3,4 @@
 --
 SET client_min_messages = WARNING;
 
-DROP EXTENSION plpythonu CASCADE;
-
-DROP EXTENSION IF EXISTS plpython2u CASCADE;
+DROP EXTENSION plpython3u CASCADE;
diff --git a/src/pl/plpython/sql/plpython_ereport.sql b/src/pl/plpython/sql/plpython_ereport.sql
index 58df2057ef5..3bcf8f5cde9 100644
--- a/src/pl/plpython/sql/plpython_ereport.sql
+++ b/src/pl/plpython/sql/plpython_ereport.sql
@@ -17,28 +17,28 @@ plpy.info('This is message text.',
 plpy.notice('notice', detail='some detail')
 plpy.warning('warning', detail='some detail')
 plpy.error('stop on error', detail='some detail', hint='some hint')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT elog_test();
 
-DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpythonu;
+DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u;
 
 DO $$
 import time;
 from datetime import date
 plpy.info('other types', detail=date(2016, 2, 26))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 plpy.info('other types', detail=basket)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- should fail
-DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u;
 
 -- raise exception in python, handle exception in plgsql
 CREATE OR REPLACE FUNCTION raise_exception(_message text, _detail text DEFAULT NULL, _hint text DEFAULT NULL,
@@ -57,7 +57,7 @@ kwargs = {
 }
 # ignore None values
 plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT raise_exception('hello', 'world');
 SELECT raise_exception('message text', 'detail text', _sqlstate => 'YY333');
@@ -128,7 +128,7 @@ try:
 except Exception as e:
     plpy.info(e.spidata)
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 try:
@@ -136,4 +136,4 @@ try:
 except Exception as e:
     plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name))
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index 88d6936fd0d..11f14ec5a7c 100644
--- a/src/pl/plpython/sql/plpython_error.sql
+++ b/src/pl/plpython/sql/plpython_error.sql
@@ -7,7 +7,7 @@
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 /* With check_function_bodies = false the function should get defined
  * and the error reported when called
@@ -17,7 +17,7 @@ SET check_function_bodies = false;
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 SELECT python_syntax_error();
 /* Run the function twice to check if the hashtable entry gets cleaned up */
@@ -30,7 +30,7 @@ RESET check_function_bodies;
 CREATE FUNCTION sql_syntax_error() RETURNS text
         AS
 'plpy.execute("syntax error")'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 SELECT sql_syntax_error();
 
@@ -40,7 +40,7 @@ SELECT sql_syntax_error();
 CREATE FUNCTION exception_index_invalid(text) RETURNS text
 	AS
 'return args[1]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT exception_index_invalid('test');
 
@@ -51,7 +51,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text
 	AS
 'rv = plpy.execute("SELECT test5(''foo'')")
 return rv[0]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT exception_index_invalid_nested();
 
@@ -68,7 +68,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_uncaught('rick');
 
@@ -90,7 +90,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_caught('rick');
 
@@ -111,7 +111,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_reraised('rick');
 
@@ -127,7 +127,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT valid_type('rick');
 
@@ -147,7 +147,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_error();
 
@@ -167,7 +167,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_error_raise();
 
@@ -187,7 +187,7 @@ def fun3():
 fun3()
 return "you''ve been warned"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_warning();
 
@@ -196,7 +196,7 @@ SELECT nested_warning();
 CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
 $$
 plpy.nonexistent
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT toplevel_attribute_error();
 
@@ -213,7 +213,7 @@ def third():
   plpy.execute("select sql_error()")
 
 first()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
 begin
@@ -229,7 +229,7 @@ $$ LANGUAGE plpgsql;
 
 CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
 plpy.execute("select sql_error()")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT python_traceback();
 SELECT sql_error();
@@ -251,7 +251,7 @@ except spiexceptions.NotNullViolation as e:
     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
 except spiexceptions.UniqueViolation as e:
     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT specific_exception(2);
 SELECT specific_exception(NULL);
@@ -262,7 +262,7 @@ SELECT specific_exception(2);
 CREATE FUNCTION python_unique_violation() RETURNS void AS $$
 plpy.execute("insert into specific values (1)")
 plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
 begin
@@ -283,7 +283,7 @@ CREATE FUNCTION manual_subxact() RETURNS void AS $$
 plpy.execute("savepoint save")
 plpy.execute("create table foo(x integer)")
 plpy.execute("rollback to save")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT manual_subxact();
 
@@ -295,7 +295,7 @@ rollback = plpy.prepare("rollback to save")
 plpy.execute(save)
 plpy.execute("create table foo(x integer)")
 plpy.execute(rollback)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT manual_subxact_prepared();
 
@@ -303,7 +303,7 @@ SELECT manual_subxact_prepared();
  */
 CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
 raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 BEGIN
@@ -319,7 +319,7 @@ CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
 exc = plpy.spiexceptions.DivisionByZero()
 exc.sqlstate = 'SILLY'
 raise exc
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 BEGIN
@@ -332,14 +332,14 @@ $$ LANGUAGE plpgsql;
 /* test the context stack trace for nested execution levels
  */
 CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
 plpy.execute("SELECT notice_innerfunc()")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 \set SHOW_CONTEXT always
 
diff --git a/src/pl/plpython/sql/plpython_global.sql b/src/pl/plpython/sql/plpython_global.sql
index 32502b41eee..96d20492861 100644
--- a/src/pl/plpython/sql/plpython_global.sql
+++ b/src/pl/plpython/sql/plpython_global.sql
@@ -9,7 +9,7 @@ CREATE FUNCTION global_test_one() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_one"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 CREATE FUNCTION global_test_two() returns text
     AS
@@ -18,7 +18,7 @@ CREATE FUNCTION global_test_two() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_two"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 CREATE FUNCTION static_test() returns int4
@@ -29,7 +29,7 @@ else:
 	SD["call"] = 1
 return SD["call"]
 '
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 SELECT static_test();
diff --git a/src/pl/plpython/sql/plpython_import.sql b/src/pl/plpython/sql/plpython_import.sql
index ec887677e1e..3031eef2e69 100644
--- a/src/pl/plpython/sql/plpython_import.sql
+++ b/src/pl/plpython/sql/plpython_import.sql
@@ -7,7 +7,7 @@ CREATE FUNCTION import_fail() returns text
 except ImportError:
 	return "failed as expected"
 return "succeeded, that wasn''t supposed to happen"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 CREATE FUNCTION import_succeed() returns text
@@ -28,7 +28,7 @@ except Exception as ex:
 	plpy.notice("import failed -- %s" % str(ex))
 	return "failed, that wasn''t supposed to happen"
 return "succeeded, as expected"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 CREATE FUNCTION import_test_one(p text) RETURNS text
 	AS
@@ -39,7 +39,7 @@ except ImportError:
     import sha
     digest = sha.new(p)
 return digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION import_test_two(u users) RETURNS text
 	AS
@@ -51,7 +51,7 @@ except ImportError:
     import sha
     digest = sha.new(plain);
 return "sha hash of " + plain + " is " + digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 -- import python modules
diff --git a/src/pl/plpython/sql/plpython_newline.sql b/src/pl/plpython/sql/plpython_newline.sql
index f9cee9491bb..cb22ba923f9 100644
--- a/src/pl/plpython/sql/plpython_newline.sql
+++ b/src/pl/plpython/sql/plpython_newline.sql
@@ -4,15 +4,15 @@
 
 CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS
 E'x = 100\ny = 23\nreturn x + y\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS
 E'x = 100\ry = 23\rreturn x + y\r'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS
 E'x = 100\r\ny = 23\r\nreturn x + y\r\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 
 SELECT newline_lf();
diff --git a/src/pl/plpython/sql/plpython_params.sql b/src/pl/plpython/sql/plpython_params.sql
index ee75c4dc410..8bab4888592 100644
--- a/src/pl/plpython/sql/plpython_params.sql
+++ b/src/pl/plpython/sql/plpython_params.sql
@@ -4,13 +4,13 @@
 
 CREATE FUNCTION test_param_names0(integer, integer) RETURNS int AS $$
 return args[0] + args[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_param_names1(a0 integer, a1 text) RETURNS boolean AS $$
 assert a0 == args[0]
 assert a1 == args[1]
 return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_param_names2(u users) RETURNS text AS $$
 assert u == args[0]
@@ -22,7 +22,7 @@ if isinstance(u, dict):
 else:
     s = str(u)
 return s
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- use deliberately wrong parameter names
 CREATE FUNCTION test_param_names3(a0 integer) RETURNS boolean AS $$
@@ -32,7 +32,7 @@ try:
 except NameError as e:
 	assert e.args[0].find("a1") > -1
 	return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 SELECT test_param_names0(2,7);
diff --git a/src/pl/plpython/sql/plpython_quote.sql b/src/pl/plpython/sql/plpython_quote.sql
index 346b5485daf..a1133e7e266 100644
--- a/src/pl/plpython/sql/plpython_quote.sql
+++ b/src/pl/plpython/sql/plpython_quote.sql
@@ -9,7 +9,7 @@ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
         return plpy.quote_ident(t)
     else:
         raise plpy.Error("unrecognized quote type %s" % how)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT quote(t, 'literal') FROM (VALUES
        ('abc'),
diff --git a/src/pl/plpython/sql/plpython_record.sql b/src/pl/plpython/sql/plpython_record.sql
index 9bab4c9e82d..52bad8bccea 100644
--- a/src/pl/plpython/sql/plpython_record.sql
+++ b/src/pl/plpython/sql/plpython_record.sql
@@ -27,7 +27,7 @@ elif typ == 'obj':
 	type_record.first = first
 	type_record.second = second
 	return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
 if retnull:
@@ -45,20 +45,20 @@ elif typ == 'obj':
 	return type_record
 elif typ == 'str':
 	return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
 return first + '_in_to_out';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 -- Test tuple returning functions
@@ -136,14 +136,14 @@ SELECT * FROM test_type_record_as('obj', 'one', 1, false);
 
 CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
     return { 'first': 'first' }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error1();
 
 
 CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
     return [ 'first' ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error2();
 
@@ -152,12 +152,12 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
     class type_record: pass
     type_record.first = 'first'
     return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error3();
 
 CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
     return 'foo'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error4();
diff --git a/src/pl/plpython/sql/plpython_setof.sql b/src/pl/plpython/sql/plpython_setof.sql
index 16c2eef0ad6..4cfb10192c0 100644
--- a/src/pl/plpython/sql/plpython_setof.sql
+++ b/src/pl/plpython/sql/plpython_setof.sql
@@ -4,21 +4,21 @@
 
 CREATE FUNCTION test_setof_error() RETURNS SETOF text AS $$
 return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_setof_error();
 
 
 CREATE FUNCTION test_setof_as_list(count integer, content text) RETURNS SETOF text AS $$
 return [ content ]*count
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_as_tuple(count integer, content text) RETURNS SETOF text AS $$
 t = ()
 for i in range(count):
 	t += ( content, )
 return t
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_as_iterator(count integer, content text) RETURNS SETOF text AS $$
 class producer:
@@ -27,13 +27,13 @@ class producer:
 		self.icount = icount
 	def __iter__ (self):
 		return self
-	def next (self):
+	def __next__ (self):
 		if self.icount == 0:
 			raise StopIteration
 		self.icount -= 1
 		return self.icontent
 return producer(count, content)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_spi_in_iterator() RETURNS SETOF text AS
 $$
@@ -42,7 +42,7 @@ $$
         yield s
         plpy.execute('select 2')
 $$
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 
 -- Test set returning functions
@@ -69,7 +69,7 @@ global x
 while x <= lim:
     yield x
     x = x + 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT ugly(1, 5);
 
@@ -81,7 +81,7 @@ CREATE OR REPLACE FUNCTION get_user_records()
 RETURNS SETOF users
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT get_user_records();
 SELECT * FROM get_user_records();
@@ -91,7 +91,7 @@ CREATE OR REPLACE FUNCTION get_user_records2()
 RETURNS TABLE(fname text, lname text, username text, userid int)
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT get_user_records2();
 SELECT * FROM get_user_records2();
diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql
index dd77833ed56..112add93fc9 100644
--- a/src/pl/plpython/sql/plpython_spi.sql
+++ b/src/pl/plpython/sql/plpython_spi.sql
@@ -7,19 +7,19 @@ CREATE FUNCTION nested_call_one(a text) RETURNS text
 'q = "SELECT nested_call_two(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 CREATE FUNCTION nested_call_two(a text) RETURNS text
 	AS
 'q = "SELECT nested_call_three(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 CREATE FUNCTION nested_call_three(a text) RETURNS text
 	AS
 'return a'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 -- some spi stuff
 
@@ -35,7 +35,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
 	AS
@@ -49,7 +49,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
 	AS
@@ -64,7 +64,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION join_sequences(s sequences) RETURNS text
 	AS
@@ -77,7 +77,7 @@ for r in rv:
 	seq = seq + r["sequence"]
 return seq
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
 	AS
@@ -86,7 +86,7 @@ if a > 1:
     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
 return a + r
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 --
 -- spi and nested calls
@@ -120,7 +120,7 @@ if result.status() > 0:
    return result.nrows()
 else:
    return None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
 SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
@@ -129,7 +129,7 @@ CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return result.nrows()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_nrows_test($$SELECT 1$$);
 SELECT result_nrows_test($$CREATE TEMPORARY TABLE foo2 (a int, b text)$$);
@@ -140,7 +140,7 @@ CREATE FUNCTION result_len_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return len(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_len_test($$SELECT 1$$);
 SELECT result_len_test($$CREATE TEMPORARY TABLE foo3 (a int, b text)$$);
@@ -170,7 +170,7 @@ except TypeError:
 else:
     assert False, "TypeError not raised"
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_subscript_test();
 
@@ -180,7 +180,7 @@ result = plpy.execute("select 1 where false")
 
 plpy.info(result[:])
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_empty_test();
 
@@ -189,7 +189,7 @@ AS $$
 plan = plpy.prepare(cmd)
 result = plpy.execute(plan)
 return str(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
 SELECT result_str_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
@@ -203,13 +203,13 @@ for row in res:
     if row['lname'] == 'doe':
         does += 1
 return does
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
 res.close()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -228,7 +228,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users order by fname")
@@ -242,7 +242,7 @@ except AttributeError:
 assert item['fname'] == 'rick'
 
 assert len(res.fetch(2)) == 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -253,7 +253,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION next_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -267,7 +267,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users where false")
@@ -281,7 +281,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
 plan = plpy.prepare(
@@ -291,13 +291,13 @@ for row in plpy.cursor(plan, ["w"]):
     yield row['fname']
 for row in plan.cursor(["j"]):
     yield row['fname']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
                     ["text"])
 c = plpy.cursor(plan, ["a", "b"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TYPE test_composite_type AS (
   a1 int,
@@ -308,7 +308,7 @@ CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS
 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
 return res[0]["c1"]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT simple_cursor_test();
 SELECT double_cursor_close();
diff --git a/src/pl/plpython/sql/plpython_subtransaction.sql b/src/pl/plpython/sql/plpython_subtransaction.sql
index cc4b1ae102b..c65c380f40c 100644
--- a/src/pl/plpython/sql/plpython_subtransaction.sql
+++ b/src/pl/plpython/sql/plpython_subtransaction.sql
@@ -17,7 +17,7 @@ with plpy.subtransaction():
         plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
     elif what_error == "Python":
         raise Exception("Python exception")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_ctx_test();
 SELECT * FROM subtransaction_tbl;
@@ -45,7 +45,7 @@ with plpy.subtransaction():
             raise
         plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_nested_test();
 SELECT * FROM subtransaction_tbl;
@@ -65,7 +65,7 @@ with plpy.subtransaction():
     plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
     plpy.execute("SELECT subtransaction_nested_test('t')")
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_deeply_nested_test();
 SELECT * FROM subtransaction_tbl;
@@ -76,25 +76,25 @@ TRUNCATE subtransaction_tbl;
 CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
 AS $$
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__exit__(None, None, None)
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
 AS $$
@@ -102,7 +102,7 @@ s = plpy.subtransaction()
 s.__enter__()
 s.__exit__(None, None, None)
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
 AS $$
@@ -110,14 +110,14 @@ s = plpy.subtransaction()
 s.__enter__()
 s.__enter__()
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- No warnings here, as the subtransaction gets indeed closed
 CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
 AS $$
 with plpy.subtransaction() as s:
     s.__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
 AS $$
@@ -126,7 +126,7 @@ try:
         s.__exit__(None, None, None)
 except ValueError as e:
     raise ValueError(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_exit_without_enter();
 SELECT subtransaction_enter_without_exit();
@@ -159,7 +159,7 @@ try:
     plpy.execute(p, ["wrong"])
 except plpy.SPIError:
     plpy.warning("Caught a SPI error")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_mix_explicit_and_implicit();
 SELECT * FROM subtransaction_tbl;
@@ -172,7 +172,7 @@ AS $$
 s = plpy.subtransaction()
 s.enter()
 s.exit(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_alternative_names();
 
@@ -186,7 +186,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT try_catch_inside_subtransaction();
 SELECT * FROM subtransaction_tbl;
@@ -202,7 +202,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT pk_violation_inside_subtransaction();
 SELECT * FROM subtransaction_tbl;
@@ -217,7 +217,7 @@ with plpy.subtransaction():
     cur.fetch(10)
 fetched = cur.fetch(10);
 return int(fetched[5]["i"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
 try:
@@ -229,7 +229,7 @@ except plpy.SPIError:
     fetched = cur.fetch(10)
     return int(fetched[5]["i"])
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
 try:
@@ -243,7 +243,7 @@ except plpy.SPIError:
     fetched = cur.fetch(5)
     return fetched[2]["i"]
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
 try:
@@ -254,7 +254,7 @@ except plpy.SPIError:
     cur.close()
     return True
 return False # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT cursor_in_subxact();
 SELECT cursor_aborted_subxact();
diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql
index 5f1be9c94a8..aa22a274155 100644
--- a/src/pl/plpython/sql/plpython_test.sql
+++ b/src/pl/plpython/sql/plpython_test.sql
@@ -1,13 +1,13 @@
 -- first some tests of basic functionality
-CREATE EXTENSION plpython2u;
+CREATE EXTENSION plpython3u;
 
 -- really stupid function just to get the module loaded
-CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
+CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 
 select stupid();
 
 -- check 2/3 versioning
-CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
+CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 
 select stupidn();
 
@@ -21,7 +21,7 @@ for key in keys:
     out.append("%s: %s" % (key, u[key]))
 words = a1 + " " + a2 + " => {" + ", ".join(out) + "}"
 return words'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 select "Argument test #1"(users, fname, lname) from users where lname = 'doe' order by 1;
 
@@ -32,7 +32,7 @@ $$
 contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
 contents.sort()
 return contents
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select module_contents();
 
@@ -47,6 +47,6 @@ plpy.info('info', 37, [1, 2, 3])
 plpy.notice('notice')
 plpy.warning('warning')
 plpy.error('error')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT elog_test_basic();
diff --git a/src/pl/plpython/sql/plpython_transaction.sql b/src/pl/plpython/sql/plpython_transaction.sql
index 33b37e5b7fc..f9062254572 100644
--- a/src/pl/plpython/sql/plpython_transaction.sql
+++ b/src/pl/plpython/sql/plpython_transaction.sql
@@ -2,7 +2,7 @@ CREATE TABLE test1 (a int, b text);
 
 
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -20,7 +20,7 @@ SELECT * FROM test1;
 TRUNCATE test1;
 
 DO
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -37,7 +37,7 @@ TRUNCATE test1;
 
 -- not allowed in a function
 CREATE FUNCTION transaction_test2() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -55,7 +55,7 @@ SELECT * FROM test1;
 
 -- also not allowed if procedure is called from a function
 CREATE FUNCTION transaction_test3() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("CALL transaction_test1()")
 return 1
@@ -68,9 +68,9 @@ SELECT * FROM test1;
 
 -- DO block inside function
 CREATE FUNCTION transaction_test4() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 return 1
 $$;
 
@@ -78,7 +78,7 @@ SELECT transaction_test4();
 
 
 -- commit inside subtransaction (prohibited)
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 s = plpy.subtransaction()
 s.enter()
 plpy.commit()
@@ -91,7 +91,7 @@ INSERT INTO test2 VALUES (0), (1), (2), (3), (4);
 
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.commit()
@@ -106,7 +106,7 @@ SELECT * FROM pg_cursors;
 -- error in cursor loop with commit
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x'])
     plpy.commit()
@@ -120,7 +120,7 @@ SELECT * FROM pg_cursors;
 -- rollback inside cursor loop
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.rollback()
@@ -134,7 +134,7 @@ SELECT * FROM pg_cursors;
 -- first commit then rollback inside cursor loop
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     if row['x'] % 2 == 0:
diff --git a/src/pl/plpython/sql/plpython_trigger.sql b/src/pl/plpython/sql/plpython_trigger.sql
index 19852dc5851..e5504b9ab1d 100644
--- a/src/pl/plpython/sql/plpython_trigger.sql
+++ b/src/pl/plpython/sql/plpython_trigger.sql
@@ -16,7 +16,7 @@ if TD["new"]["fname"] == "william":
 	TD["new"]["fname"] = TD["args"][0]
 	rv = "MODIFY"
 return rv'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE FUNCTION users_update() returns trigger
@@ -25,7 +25,7 @@ CREATE FUNCTION users_update() returns trigger
 	if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
 		return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE FUNCTION users_delete() RETURNS trigger
@@ -33,7 +33,7 @@ CREATE FUNCTION users_delete() RETURNS trigger
 'if TD["old"]["fname"] == TD["args"][0]:
 	return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
@@ -72,7 +72,7 @@ CREATE TABLE trigger_test_generated (
         j int GENERATED ALWAYS AS (i * 2) STORED
 );
 
-CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
 
 if 'relid' in TD:
 	TD['relid'] = "bogus:12345"
@@ -157,7 +157,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid1() RETURNS trigger
 AS $$
     return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger1
 BEFORE INSERT ON trigger_test
@@ -173,7 +173,7 @@ DROP TRIGGER stupid_trigger1 ON trigger_test;
 CREATE FUNCTION stupid2() RETURNS trigger
 AS $$
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger2
 BEFORE DELETE ON trigger_test
@@ -191,7 +191,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid3() RETURNS trigger
 AS $$
     return "foo"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
@@ -206,8 +206,8 @@ DROP TRIGGER stupid_trigger3 ON trigger_test;
 
 CREATE FUNCTION stupid3u() RETURNS trigger
 AS $$
-    return u"foo"
-$$ LANGUAGE plpythonu;
+    return "foo"
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
@@ -224,7 +224,7 @@ CREATE FUNCTION stupid4() RETURNS trigger
 AS $$
     del TD["new"]
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger4
 BEFORE UPDATE ON trigger_test
@@ -241,7 +241,7 @@ CREATE FUNCTION stupid5() RETURNS trigger
 AS $$
     TD["new"] = ['foo', 'bar']
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger5
 BEFORE UPDATE ON trigger_test
@@ -258,7 +258,7 @@ CREATE FUNCTION stupid6() RETURNS trigger
 AS $$
     TD["new"] = {1: 'foo', 2: 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger6
 BEFORE UPDATE ON trigger_test
@@ -275,7 +275,7 @@ CREATE FUNCTION stupid7() RETURNS trigger
 AS $$
     TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
@@ -290,9 +290,9 @@ DROP TRIGGER stupid_trigger7 ON trigger_test;
 
 CREATE FUNCTION stupid7u() RETURNS trigger
 AS $$
-    TD["new"] = {u'v': 'foo', u'a': 'bar'}
+    TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
@@ -318,7 +318,7 @@ CREATE FUNCTION test_null() RETURNS trigger
 AS $$
     TD["new"]['v'] = None
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER test_null_trigger
 BEFORE UPDATE ON trigger_test
@@ -341,7 +341,7 @@ SET DateStyle = 'ISO';
 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
 
@@ -365,7 +365,7 @@ CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
     TD['new']['f1'] = (3, False)
     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
@@ -380,7 +380,7 @@ CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
 
 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
@@ -399,7 +399,7 @@ CREATE TABLE composite_trigger_nested_test(c comp3);
 
 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
@@ -410,7 +410,7 @@ INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'
 SELECT * FROM composite_trigger_nested_test;
 
 -- check that using a function as a trigger over two tables works correctly
-CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
     TD["new"]["data"] = '1234'
     return 'MODIFY'
 $$;
@@ -432,7 +432,7 @@ SELECT * FROM b;
 CREATE TABLE transition_table_test (id int, name text);
 INSERT INTO transition_table_test VALUES (1, 'a');
 
-CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
 $$
     rv = plpy.execute("SELECT * FROM old_table")
     assert(rv.nrows() == 1)
@@ -455,7 +455,7 @@ DROP FUNCTION transition_table_test_f();
 -- dealing with generated columns
 
 CREATE FUNCTION generated_test_func1() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 TD['new']['j'] = 5  # not allowed
 return 'MODIFY'
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
index 0d207d9c015..40f4f79d99f 100644
--- a/src/pl/plpython/sql/plpython_types.sql
+++ b/src/pl/plpython/sql/plpython_types.sql
@@ -9,7 +9,7 @@
 CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bool(true);
 SELECT * FROM test_type_conversion_bool(false);
@@ -35,7 +35,7 @@ elif n == 5:
    ret = [0]
 plpy.info(ret, not not ret)
 return ret
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bool_other(0);
 SELECT * FROM test_type_conversion_bool_other(1);
@@ -48,7 +48,7 @@ SELECT * FROM test_type_conversion_bool_other(5);
 CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_char('a');
 SELECT * FROM test_type_conversion_char(null);
@@ -57,7 +57,7 @@ SELECT * FROM test_type_conversion_char(null);
 CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int2(100::int2);
 SELECT * FROM test_type_conversion_int2(-100::int2);
@@ -67,7 +67,7 @@ SELECT * FROM test_type_conversion_int2(null);
 CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int4(100);
 SELECT * FROM test_type_conversion_int4(-100);
@@ -77,7 +77,7 @@ SELECT * FROM test_type_conversion_int4(null);
 CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int8(100);
 SELECT * FROM test_type_conversion_int8(-100);
@@ -90,7 +90,7 @@ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
 # between decimal and cdecimal
 plpy.info(str(x), x.__class__.__name__)
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_numeric(100);
 SELECT * FROM test_type_conversion_numeric(-100);
@@ -105,7 +105,7 @@ SELECT * FROM test_type_conversion_numeric(null);
 CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_float4(100);
 SELECT * FROM test_type_conversion_float4(-100);
@@ -116,7 +116,7 @@ SELECT * FROM test_type_conversion_float4(null);
 CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_float8(100);
 SELECT * FROM test_type_conversion_float8(-100);
@@ -128,7 +128,7 @@ SELECT * FROM test_type_conversion_float8(100100100.654321);
 CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_oid(100);
 SELECT * FROM test_type_conversion_oid(2147483649);
@@ -138,7 +138,7 @@ SELECT * FROM test_type_conversion_oid(null);
 CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_text('hello world');
 SELECT * FROM test_type_conversion_text(null);
@@ -147,7 +147,7 @@ SELECT * FROM test_type_conversion_text(null);
 CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bytea('hello world');
 SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
@@ -157,7 +157,7 @@ SELECT * FROM test_type_conversion_bytea(null);
 CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
 import marshal
 return marshal.dumps('hello world')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
 import marshal
@@ -165,7 +165,7 @@ try:
     return marshal.loads(x)
 except ValueError as e:
     return 'FAILED: ' + str(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
 
@@ -178,7 +178,7 @@ CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
 
 CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_booltrue(true, true);
 SELECT * FROM test_type_conversion_booltrue(false, true);
@@ -190,7 +190,7 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
 CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
 SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
@@ -201,7 +201,7 @@ CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
 
 CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_nnint(10, 20);
 SELECT * FROM test_type_conversion_nnint(null, 20);
@@ -213,7 +213,7 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
 CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
 SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
@@ -229,7 +229,7 @@ SELECT * FROM test_type_conversion_bytea10('hello word', null);
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
@@ -243,14 +243,14 @@ SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
 CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
 
 CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
             [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
@@ -258,7 +258,7 @@ SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',
 CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
             [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
@@ -270,7 +270,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemint4(8,5,3,2);
 
@@ -278,7 +278,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemint8(5,5,3,2);
 
@@ -286,7 +286,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemfloat4(6,5,3,2);
 
@@ -294,14 +294,14 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemfloat8(7,5,3,2);
 
 CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
 SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
@@ -310,59 +310,59 @@ SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar
 CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
 
 
 CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_mixed1();
 
 
 CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_mixed2();
 
 CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
 return [[1,2,3],[4,5]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_mdarray_malformed();
 
 CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
 return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_mdarray_toodeep();
 
 
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_record();
 
 
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_string();
 
 CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
 return ('abc', 'def')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_tuple();
 
 CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
 return 5
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_error();
 
@@ -376,14 +376,14 @@ CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AN
 CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
 SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
 
 CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
 return [2,1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain_check_violation();
 
 
@@ -394,13 +394,13 @@ SELECT * FROM test_type_conversion_array_domain_check_violation();
 CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_read_uint2_array(array[1::uint2]);
 
 CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_build_uint2_array(1::int2);
 select test_build_uint2_array(-1::int2);  -- fail
@@ -413,7 +413,7 @@ select test_build_uint2_array(-1::int2);  -- fail
 CREATE FUNCTION test_type_conversion_domain_array(x integer[])
   RETURNS ordered_pair_domain[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_domain_array(array[2,4]);
 select test_type_conversion_domain_array(array[4,2]);  -- fail
@@ -422,7 +422,7 @@ CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
   RETURNS integer AS $$
 plpy.info(x, type(x))
 return x[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_domain_array2(array[2,4]);
 select test_type_conversion_domain_array2(array[4,2]);  -- fail
@@ -431,7 +431,7 @@ CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
   RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
 
@@ -450,7 +450,7 @@ INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
 
 CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
 return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT name, test_composite_table_input(employee.*) FROM employee;
 
@@ -470,7 +470,7 @@ CREATE TYPE named_pair AS (
 
 CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
 return sum(p.values())
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_composite_type_input(row(1, 2));
 
@@ -487,7 +487,7 @@ CREATE TYPE nnint_container AS (f1 int, f2 nnint);
 
 CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
 return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT nnint_test(null, 3);
 SELECT nnint_test(3, null);  -- fail
@@ -501,21 +501,21 @@ CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
 
 CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
 return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT read_ordered_named_pair(row(1, 2));
 SELECT read_ordered_named_pair(row(2, 1));  -- fail
 
 CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
 return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT build_ordered_named_pair(1,2);
 SELECT build_ordered_named_pair(2,1);  -- fail
 
 CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
 return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT build_ordered_named_pairs(1,2);
 SELECT build_ordered_named_pairs(2,1);  -- fail
@@ -526,7 +526,7 @@ SELECT build_ordered_named_pairs(2,1);  -- fail
 --
 
 CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
 rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
@@ -537,7 +537,7 @@ SELECT test_prep_bool_input(); -- 1
 
 
 CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
 rv = plpy.execute(plan, [0], 5)
@@ -549,7 +549,7 @@ SELECT test_prep_bool_output(); -- false
 
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
 rv = plpy.execute(plan, [bb], 5)
@@ -560,7 +560,7 @@ SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated
 
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
 rv = plpy.execute(plan, [], 5)
diff --git a/src/pl/plpython/sql/plpython_unicode.sql b/src/pl/plpython/sql/plpython_unicode.sql
index a11e5eeaa21..14f7b4e0053 100644
--- a/src/pl/plpython/sql/plpython_unicode.sql
+++ b/src/pl/plpython/sql/plpython_unicode.sql
@@ -14,28 +14,28 @@ CREATE TABLE unicode_test (
 );
 
 CREATE FUNCTION unicode_return() RETURNS text AS E'
-return u"\\xA0"
-' LANGUAGE plpythonu;
+return "\\xA0"
+' LANGUAGE plpython3u;
 
 CREATE FUNCTION unicode_trigger() RETURNS trigger AS E'
-TD["new"]["testvalue"] = u"\\xA0"
+TD["new"]["testvalue"] = "\\xA0"
 return "MODIFY"
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test
   FOR EACH ROW EXECUTE PROCEDURE unicode_trigger();
 
 CREATE FUNCTION unicode_plan1() RETURNS text AS E'
 plan = plpy.prepare("SELECT $1 AS testvalue", ["text"])
-rv = plpy.execute(plan, [u"\\xA0"], 1)
+rv = plpy.execute(plan, ["\\xA0"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 CREATE FUNCTION unicode_plan2() RETURNS text AS E'
-plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"])
+plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"])
 rv = plpy.execute(plan, ["foo", "bar"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 
 SELECT unicode_return();
diff --git a/src/pl/plpython/sql/plpython_void.sql b/src/pl/plpython/sql/plpython_void.sql
index 77d7f59e4c7..5a1a6711fb0 100644
--- a/src/pl/plpython/sql/plpython_void.sql
+++ b/src/pl/plpython/sql/plpython_void.sql
@@ -4,16 +4,16 @@
 
 CREATE FUNCTION test_void_func1() RETURNS void AS $$
 x = 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- illegal: can't return non-None value in void-returning func
 CREATE FUNCTION test_void_func2() RETURNS void AS $$
 return 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_return_none() RETURNS int AS $$
 None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 -- Tests for functions returning void
diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile
index 6af097ae68b..9d88cda1d0f 100644
--- a/contrib/hstore_plpython/Makefile
+++ b/contrib/hstore_plpython/Makefile
@@ -6,11 +6,10 @@ OBJS = \
 	hstore_plpython.o
 PGFILEDESC = "hstore_plpython - hstore transform for plpython"
 
-EXTENSION = hstore_plpythonu hstore_plpython2u hstore_plpython3u
-DATA = hstore_plpythonu--1.0.sql hstore_plpython2u--1.0.sql hstore_plpython3u--1.0.sql
+EXTENSION = hstore_plpython3u
+DATA = hstore_plpython3u--1.0.sql
 
 REGRESS = hstore_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 PG_CPPFLAGS = $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
@@ -37,9 +36,4 @@ SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
 
 REGRESS_OPTS += --load-extension=hstore
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=hstore_plpythonu
-endif
 EXTRA_INSTALL += contrib/hstore
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out
index ecf1dd61bc1..bf238701fec 100644
--- a/contrib/hstore_plpython/expected/hstore_plpython.out
+++ b/contrib/hstore_plpython/expected/hstore_plpython.out
@@ -1,8 +1,8 @@
-CREATE EXTENSION hstore_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION hstore_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 -- test hstore -> python
 CREATE FUNCTION test1(val hstore) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -18,7 +18,7 @@ INFO:  [('aa', 'bb'), ('cc', None)]
 
 -- the same with the versioned language name
 CREATE FUNCTION test1n(val hstore) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -34,7 +34,7 @@ INFO:  [('aa', 'bb'), ('cc', None)]
 
 -- test hstore[] -> python
 CREATE FUNCTION test1arr(val hstore[]) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}])
@@ -48,7 +48,7 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']);
 
 -- test python -> hstore
 CREATE FUNCTION test2(a int, b text) RETURNS hstore
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = {'a': a, 'b': b, 'c': None}
@@ -65,14 +65,14 @@ SELECT test2(1, 'boo');
 CREATE OR REPLACE FUNCTION public.test2(a integer, b text)
  RETURNS hstore
  TRANSFORM FOR TYPE hstore
- LANGUAGE plpythonu
+ LANGUAGE plpython3u
 AS $function$
 val = {'a': a, 'b': b, 'c': None}
 return val
 $function$
 -- test python -> hstore[]
 CREATE FUNCTION test2arr() RETURNS hstore[]
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
@@ -87,7 +87,7 @@ SELECT test2arr();
 -- test python -> domain over hstore
 CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
 CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 return {'a': 1, fn: 'boo', 'c': None}
@@ -104,7 +104,7 @@ CONTEXT:  while creating return value
 PL/Python function "test2dom"
 -- test as part of prepare/execute
 CREATE FUNCTION test3() RETURNS void
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1")
@@ -131,7 +131,7 @@ SELECT * FROM test1;
 (1 row)
 
 CREATE FUNCTION test4() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}})
diff --git a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql b/contrib/hstore_plpython/hstore_plpython2u--1.0.sql
deleted file mode 100644
index 800765f3f0c..00000000000
--- a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/hstore_plpython/hstore_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION hstore_plpython2u" to load this file. \quit
-
-CREATE FUNCTION hstore_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'hstore_to_plpython';
-
-CREATE FUNCTION plpython2_to_hstore(val internal) RETURNS hstore
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'plpython_to_hstore';
-
-CREATE TRANSFORM FOR hstore LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION hstore_to_plpython2(internal),
-    TO SQL WITH FUNCTION plpython2_to_hstore(internal)
-);
-
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython2u IS 'transform between hstore and Python dict';
diff --git a/contrib/hstore_plpython/hstore_plpython2u.control b/contrib/hstore_plpython/hstore_plpython2u.control
deleted file mode 100644
index ed905671123..00000000000
--- a/contrib/hstore_plpython/hstore_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# hstore_plpython2u extension
-comment = 'transform between hstore and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/hstore_plpython2'
-relocatable = true
-requires = 'hstore,plpython2u'
diff --git a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql b/contrib/hstore_plpython/hstore_plpythonu--1.0.sql
deleted file mode 100644
index 52832912abc..00000000000
--- a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/hstore_plpython/hstore_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION hstore_plpythonu" to load this file. \quit
-
-CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR hstore LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION hstore_to_plpython(internal),
-    TO SQL WITH FUNCTION plpython_to_hstore(internal)
-);
-
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'transform between hstore and Python dict';
diff --git a/contrib/hstore_plpython/hstore_plpythonu.control b/contrib/hstore_plpython/hstore_plpythonu.control
deleted file mode 100644
index 8e9b35e43bf..00000000000
--- a/contrib/hstore_plpython/hstore_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# hstore_plpythonu extension
-comment = 'transform between hstore and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/hstore_plpython2'
-relocatable = true
-requires = 'hstore,plpythonu'
diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql
index b6d98b7dd53..a9cfbbe13e2 100644
--- a/contrib/hstore_plpython/sql/hstore_plpython.sql
+++ b/contrib/hstore_plpython/sql/hstore_plpython.sql
@@ -1,9 +1,9 @@
-CREATE EXTENSION hstore_plpython2u CASCADE;
+CREATE EXTENSION hstore_plpython3u CASCADE;
 
 
 -- test hstore -> python
 CREATE FUNCTION test1(val hstore) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -16,7 +16,7 @@ SELECT test1('aa=>bb, cc=>NULL'::hstore);
 
 -- the same with the versioned language name
 CREATE FUNCTION test1n(val hstore) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -29,7 +29,7 @@ SELECT test1n('aa=>bb, cc=>NULL'::hstore);
 
 -- test hstore[] -> python
 CREATE FUNCTION test1arr(val hstore[]) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}])
@@ -41,7 +41,7 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']);
 
 -- test python -> hstore
 CREATE FUNCTION test2(a int, b text) RETURNS hstore
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = {'a': a, 'b': b, 'c': None}
@@ -56,7 +56,7 @@ SELECT test2(1, 'boo');
 
 -- test python -> hstore[]
 CREATE FUNCTION test2arr() RETURNS hstore[]
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
@@ -70,7 +70,7 @@ SELECT test2arr();
 CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
 
 CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 return {'a': 1, fn: 'boo', 'c': None}
@@ -82,7 +82,7 @@ SELECT test2dom('bar');  -- fail
 
 -- test as part of prepare/execute
 CREATE FUNCTION test3() RETURNS void
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1")
@@ -103,7 +103,7 @@ INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL');
 SELECT * FROM test1;
 
 CREATE FUNCTION test4() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}})
diff --git a/contrib/jsonb_plpython/Makefile b/contrib/jsonb_plpython/Makefile
index ca767418943..6333ea0bbaf 100644
--- a/contrib/jsonb_plpython/Makefile
+++ b/contrib/jsonb_plpython/Makefile
@@ -8,11 +8,10 @@ PGFILEDESC = "jsonb_plpython - transform between jsonb and plpythonu"
 
 PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
-EXTENSION = jsonb_plpythonu jsonb_plpython2u jsonb_plpython3u
-DATA = jsonb_plpythonu--1.0.sql jsonb_plpython2u--1.0.sql jsonb_plpython3u--1.0.sql
+EXTENSION = jsonb_plpython3u
+DATA = jsonb_plpython3u--1.0.sql
 
 REGRESS = jsonb_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -33,9 +32,3 @@ else
 rpathdir = $(python_libdir)
 SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
-
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=jsonb_plpythonu
-endif
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/jsonb_plpython/expected/jsonb_plpython.out b/contrib/jsonb_plpython/expected/jsonb_plpython.out
index b491fe9cc68..cac963de69c 100644
--- a/contrib/jsonb_plpython/expected/jsonb_plpython.out
+++ b/contrib/jsonb_plpython/expected/jsonb_plpython.out
@@ -1,8 +1,8 @@
-CREATE EXTENSION jsonb_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION jsonb_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 -- test jsonb -> python dict
 CREATE FUNCTION test1(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -18,7 +18,7 @@ SELECT test1('{"a": 1, "c": "NULL"}'::jsonb);
 -- test jsonb -> python dict
 -- complex dict with dicts as value
 CREATE FUNCTION test1complex(val jsonb) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -34,7 +34,7 @@ SELECT test1complex('{"d": {"d": 1}}'::jsonb);
 -- test jsonb[] -> python dict
 -- dict with array as value
 CREATE FUNCTION test1arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -50,7 +50,7 @@ SELECT test1arr('{"d":[12, 1]}'::jsonb);
 -- test jsonb[] -> python list
 -- simple list
 CREATE FUNCTION test2arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -66,7 +66,7 @@ SELECT test2arr('[12, 1]'::jsonb);
 -- test jsonb[] -> python list
 -- array of dicts
 CREATE FUNCTION test3arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -81,7 +81,7 @@ SELECT test3arr('[{"a": 1, "b": 2}, {"c": 3,"d": 4}]'::jsonb);
 
 -- test jsonb int -> python int
 CREATE FUNCTION test1int(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == 1)
@@ -95,7 +95,7 @@ SELECT test1int('1'::jsonb);
 
 -- test jsonb string -> python string
 CREATE FUNCTION test1string(val jsonb) RETURNS text
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == "a")
@@ -109,7 +109,7 @@ SELECT test1string('"a"'::jsonb);
 
 -- test jsonb null -> python None
 CREATE FUNCTION test1null(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == None)
@@ -123,7 +123,7 @@ SELECT test1null('null'::jsonb);
 
 -- test python -> jsonb
 CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 as $$
 return val
@@ -238,7 +238,7 @@ SELECT roundtrip('["string", "string2"]'::jsonb);
 
 -- complex numbers -> jsonb
 CREATE FUNCTION testComplexNumbers() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 1 + 2j
@@ -250,7 +250,7 @@ CONTEXT:  while creating return value
 PL/Python function "testcomplexnumbers"
 -- range -> jsonb
 CREATE FUNCTION testRange() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = range(3)
@@ -264,7 +264,7 @@ SELECT testRange();
 
 -- 0xff -> jsonb
 CREATE FUNCTION testDecimal() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 0xff
@@ -278,7 +278,7 @@ SELECT testDecimal();
 
 -- tuple -> jsonb
 CREATE FUNCTION testTuple() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = (1, 'String', None)
@@ -292,7 +292,7 @@ SELECT testTuple();
 
 -- interesting dict -> jsonb
 CREATE FUNCTION test_dict1() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = {"a": 1, None: 2, 33: 3}
diff --git a/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql b/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
deleted file mode 100644
index 2526d14ee19..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION jsonb_plpython2u" to load this file. \quit
-
-CREATE FUNCTION jsonb_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'jsonb_to_plpython';
-
-CREATE FUNCTION plpython2_to_jsonb(val internal) RETURNS jsonb
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'plpython_to_jsonb';
-
-CREATE TRANSFORM FOR jsonb LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION jsonb_to_plpython2(internal),
-    TO SQL WITH FUNCTION plpython2_to_jsonb(internal)
-);
-
-COMMENT ON TRANSFORM FOR jsonb LANGUAGE plpython2u IS 'transform between jsonb and Python';
diff --git a/contrib/jsonb_plpython/jsonb_plpython2u.control b/contrib/jsonb_plpython/jsonb_plpython2u.control
deleted file mode 100644
index d26368316b6..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# jsonb_plpython2u extension
-comment = 'transform between jsonb and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/jsonb_plpython2'
-relocatable = true
-requires = 'plpython2u'
diff --git a/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql b/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
deleted file mode 100644
index 3fa89885a63..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION jsonb_plpythonu" to load this file. \quit
-
-CREATE FUNCTION jsonb_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_to_jsonb(val internal) RETURNS jsonb
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR jsonb LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION jsonb_to_plpython(internal),
-    TO SQL WITH FUNCTION plpython_to_jsonb(internal)
-);
-
-COMMENT ON TRANSFORM FOR jsonb LANGUAGE plpythonu IS 'transform between jsonb and Python';
diff --git a/contrib/jsonb_plpython/jsonb_plpythonu.control b/contrib/jsonb_plpython/jsonb_plpythonu.control
deleted file mode 100644
index 6f8fa4f184b..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# jsonb_plpythonu extension
-comment = 'transform between jsonb and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/jsonb_plpython2'
-relocatable = true
-requires = 'plpythonu'
diff --git a/contrib/jsonb_plpython/sql/jsonb_plpython.sql b/contrib/jsonb_plpython/sql/jsonb_plpython.sql
index 2ee1bca0a98..29dc33279a0 100644
--- a/contrib/jsonb_plpython/sql/jsonb_plpython.sql
+++ b/contrib/jsonb_plpython/sql/jsonb_plpython.sql
@@ -1,8 +1,8 @@
-CREATE EXTENSION jsonb_plpython2u CASCADE;
+CREATE EXTENSION jsonb_plpython3u CASCADE;
 
 -- test jsonb -> python dict
 CREATE FUNCTION test1(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -15,7 +15,7 @@ SELECT test1('{"a": 1, "c": "NULL"}'::jsonb);
 -- test jsonb -> python dict
 -- complex dict with dicts as value
 CREATE FUNCTION test1complex(val jsonb) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -29,7 +29,7 @@ SELECT test1complex('{"d": {"d": 1}}'::jsonb);
 -- test jsonb[] -> python dict
 -- dict with array as value
 CREATE FUNCTION test1arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -42,7 +42,7 @@ SELECT test1arr('{"d":[12, 1]}'::jsonb);
 -- test jsonb[] -> python list
 -- simple list
 CREATE FUNCTION test2arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -55,7 +55,7 @@ SELECT test2arr('[12, 1]'::jsonb);
 -- test jsonb[] -> python list
 -- array of dicts
 CREATE FUNCTION test3arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -67,7 +67,7 @@ SELECT test3arr('[{"a": 1, "b": 2}, {"c": 3,"d": 4}]'::jsonb);
 
 -- test jsonb int -> python int
 CREATE FUNCTION test1int(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == 1)
@@ -78,7 +78,7 @@ SELECT test1int('1'::jsonb);
 
 -- test jsonb string -> python string
 CREATE FUNCTION test1string(val jsonb) RETURNS text
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == "a")
@@ -89,7 +89,7 @@ SELECT test1string('"a"'::jsonb);
 
 -- test jsonb null -> python None
 CREATE FUNCTION test1null(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == None)
@@ -100,7 +100,7 @@ SELECT test1null('null'::jsonb);
 
 -- test python -> jsonb
 CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 as $$
 return val
@@ -129,7 +129,7 @@ SELECT roundtrip('["string", "string2"]'::jsonb);
 
 -- complex numbers -> jsonb
 CREATE FUNCTION testComplexNumbers() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 1 + 2j
@@ -140,7 +140,7 @@ SELECT testComplexNumbers();
 
 -- range -> jsonb
 CREATE FUNCTION testRange() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = range(3)
@@ -151,7 +151,7 @@ SELECT testRange();
 
 -- 0xff -> jsonb
 CREATE FUNCTION testDecimal() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 0xff
@@ -162,7 +162,7 @@ SELECT testDecimal();
 
 -- tuple -> jsonb
 CREATE FUNCTION testTuple() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = (1, 'String', None)
@@ -173,7 +173,7 @@ SELECT testTuple();
 
 -- interesting dict -> jsonb
 CREATE FUNCTION test_dict1() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = {"a": 1, None: 2, 33: 3}
diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile
index 12a01467721..406d2789c9c 100644
--- a/contrib/ltree_plpython/Makefile
+++ b/contrib/ltree_plpython/Makefile
@@ -6,11 +6,10 @@ OBJS = \
 	ltree_plpython.o
 PGFILEDESC = "ltree_plpython - ltree transform for plpython"
 
-EXTENSION = ltree_plpythonu ltree_plpython2u ltree_plpython3u
-DATA = ltree_plpythonu--1.0.sql ltree_plpython2u--1.0.sql ltree_plpython3u--1.0.sql
+EXTENSION = ltree_plpython3u
+DATA = ltree_plpython3u--1.0.sql
 
 REGRESS = ltree_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 PG_CPPFLAGS = $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
@@ -37,9 +36,4 @@ SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
 
 REGRESS_OPTS += --load-extension=ltree
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=ltree_plpythonu
-endif
 EXTRA_INSTALL += contrib/ltree
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/ltree_plpython/expected/ltree_plpython.out b/contrib/ltree_plpython/expected/ltree_plpython.out
index f28897fee48..bd32541fdb3 100644
--- a/contrib/ltree_plpython/expected/ltree_plpython.out
+++ b/contrib/ltree_plpython/expected/ltree_plpython.out
@@ -1,7 +1,7 @@
-CREATE EXTENSION ltree_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION ltree_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 CREATE FUNCTION test1(val ltree) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -15,7 +15,7 @@ INFO:  ['aa', 'bb', 'cc']
 (1 row)
 
 CREATE FUNCTION test1n(val ltree) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -29,7 +29,7 @@ INFO:  ['aa', 'bb', 'cc']
 (1 row)
 
 CREATE FUNCTION test2() RETURNS ltree
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 return ['foo', 'bar', 'baz']
diff --git a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql b/contrib/ltree_plpython/ltree_plpython2u--1.0.sql
deleted file mode 100644
index 5c4a7037013..00000000000
--- a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/* contrib/ltree_plpython/ltree_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION ltree_plpython2u" to load this file. \quit
-
-CREATE FUNCTION ltree_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'ltree_to_plpython';
-
-CREATE TRANSFORM FOR ltree LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION ltree_to_plpython2(internal)
-);
diff --git a/contrib/ltree_plpython/ltree_plpython2u.control b/contrib/ltree_plpython/ltree_plpython2u.control
deleted file mode 100644
index bedfd0acbad..00000000000
--- a/contrib/ltree_plpython/ltree_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# ltree_plpython2u extension
-comment = 'transform between ltree and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/ltree_plpython2'
-relocatable = true
-requires = 'ltree,plpython2u'
diff --git a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql b/contrib/ltree_plpython/ltree_plpythonu--1.0.sql
deleted file mode 100644
index ee93edf28b9..00000000000
--- a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/* contrib/ltree_plpython/ltree_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION ltree_plpythonu" to load this file. \quit
-
-CREATE FUNCTION ltree_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR ltree LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION ltree_to_plpython(internal)
-);
diff --git a/contrib/ltree_plpython/ltree_plpythonu.control b/contrib/ltree_plpython/ltree_plpythonu.control
deleted file mode 100644
index b03c89a2e6e..00000000000
--- a/contrib/ltree_plpython/ltree_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# ltree_plpythonu extension
-comment = 'transform between ltree and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/ltree_plpython2'
-relocatable = true
-requires = 'ltree,plpythonu'
diff --git a/contrib/ltree_plpython/sql/ltree_plpython.sql b/contrib/ltree_plpython/sql/ltree_plpython.sql
index 210f5428a5a..0b8d28399a6 100644
--- a/contrib/ltree_plpython/sql/ltree_plpython.sql
+++ b/contrib/ltree_plpython/sql/ltree_plpython.sql
@@ -1,8 +1,8 @@
-CREATE EXTENSION ltree_plpython2u CASCADE;
+CREATE EXTENSION ltree_plpython3u CASCADE;
 
 
 CREATE FUNCTION test1(val ltree) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -13,7 +13,7 @@ SELECT test1('aa.bb.cc'::ltree);
 
 
 CREATE FUNCTION test1n(val ltree) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -24,7 +24,7 @@ SELECT test1n('aa.bb.cc'::ltree);
 
 
 CREATE FUNCTION test2() RETURNS ltree
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 return ['foo', 'bar', 'baz']
diff --git a/configure b/configure
index 93055556585..253362eb37f 100755
--- a/configure
+++ b/configure
@@ -10346,8 +10346,8 @@ python_majorversion=`echo "$python_fullversion" | sed 's/^\([0-9]*\).*/\1/'`
 python_minorversion=`echo "$python_fullversion" | sed 's/^[0-9]*\.\([0-9]*\).*/\1/'`
 python_version=`echo "$python_fullversion" | sed 's/^\([0-9]*\.[0-9]*\).*/\1/'`
 # Reject unsupported Python versions as soon as practical.
-if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 7; then
-  as_fn_error $? "Python version $python_version is too old (version 2.7 or later is required)" "$LINENO" 5
+if test "$python_majorversion" -lt 3; then
+  as_fn_error $? "Python version $python_version is too old (version 3 or later is required)" "$LINENO" 5
 fi
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python sysconfig module" >&5
-- 
2.34.0

#41Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#40)
4 attachment(s)
Re: Time to drop plpython2?

Hi,

On 2022-02-14 22:01:31 -0800, Andres Freund wrote:

Attached the state as I have in the meson tree. Will split out the configure
test tomorrow. Might or might get through the msvc scripts and docs as well.

Attached are four patches:
1: plpython: Reject Python 2 during build configuration.
2: plpython: Remove plpythonu, plpython2u extensions.
3: plpython: Remove regression test infrastructure for Python 2.
4: WIP: plpython: Code cleanup related to removal of Python 2 support.

I think we could apply 1) now? I've adjusted installation.sgml, but there's
plenty other references to python 2 left.

The configure already only checked for major version 3, but we document
requiring python 3.2. Afaics there was no version check at all in the msvc
build before this - I've now added one, but also just for 3, for now.

I think 2) is also ok, but I'd rather commit that a buildfarm cycle after 1).

I tested the msvc portion of 3) via CI. There's still some python specific code
in vcregress.pl:plcheck(), but I think that's OK for now. We could change the
python 3 regression tests to not create the extension themselves to get rid of
that, but that doesn't really seem like an advantage?

4) needs a bit more work as noted by two FIXMEs and review comments in
/messages/by-id/3fc1211d-960b-4b2f-3e96-a6099db847fc@enterprisedb.com

There's a good chunk of docs work left.

Greetings,

Andres Freund

Attachments:

v5-0001-plpython-Reject-Python-2-during-build-configurati.patchtext/x-diff; charset=us-asciiDownload
From d391b49a7fbfd2dce793114f9aa6ba1963e5fd86 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 15 Feb 2022 12:30:20 -0800
Subject: [PATCH v5 1/4] plpython: Reject Python 2 during build configuration.

Python 2.7 went EOL 2020-01-01 and the support for Python 2 requires a fair
bit of infrastructure. Therefore we are removing Python 2 support in plpython.

This patch just rejects Python 2 during configure / mkvcbuild.pl. Future
commits will remove the code and infrastructure for Python 2 support and
adjust more of the documentation. This way we can see the buildfarm state
after the removal sooner and we can be sure that failures are due to
desupporting Python 2, rather than caused by infrastructure cleanup.

Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 config/python.m4               |  9 ++++++---
 doc/src/sgml/installation.sgml | 18 +++++-------------
 configure                      |  6 +++---
 src/tools/msvc/Mkvcbuild.pm    |  4 ++++
 4 files changed, 18 insertions(+), 19 deletions(-)

diff --git a/config/python.m4 b/config/python.m4
index c7310ee37d3..52f34070dd8 100644
--- a/config/python.m4
+++ b/config/python.m4
@@ -9,6 +9,9 @@
 # Look for Python and set the output variable 'PYTHON' if found,
 # fail otherwise.
 #
+# Since we are supporting only Python 3.x, prefer python3 to plain python.  If
+# the latter exists at all, it very possibly points to python2.
+
 # As the Python 3 transition happens and PEP 394 isn't updated, we
 # need to cater to systems that don't have unversioned "python" by
 # default.  Some systems ship with "python3" by default and perhaps
@@ -16,7 +19,7 @@
 # "python2" and "python3", in which case it's reasonable to prefer the
 # newer version.
 AC_DEFUN([PGAC_PATH_PYTHON],
-[PGAC_PATH_PROGS(PYTHON, [python python3 python2])
+[PGAC_PATH_PROGS(PYTHON, [python3 python])
 AC_ARG_VAR(PYTHON, [Python program])dnl
 if test x"$PYTHON" = x""; then
   AC_MSG_ERROR([Python not found])
@@ -37,8 +40,8 @@ python_majorversion=`echo "$python_fullversion" | sed '[s/^\([0-9]*\).*/\1/]'`
 python_minorversion=`echo "$python_fullversion" | sed '[s/^[0-9]*\.\([0-9]*\).*/\1/]'`
 python_version=`echo "$python_fullversion" | sed '[s/^\([0-9]*\.[0-9]*\).*/\1/]'`
 # Reject unsupported Python versions as soon as practical.
-if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 7; then
-  AC_MSG_ERROR([Python version $python_version is too old (version 2.7 or later is required)])
+if test "$python_majorversion" -lt 3; then
+  AC_MSG_ERROR([Python version $python_version is too old (version 3 or later is required)])
 fi
 
 AC_MSG_CHECKING([for Python sysconfig module])
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 655095f3b13..848d7caa992 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -196,11 +196,7 @@ su - postgres
       language, you need a <productname>Python</productname>
       installation with the header files and
       the <application>sysconfig</application> module.  The minimum
-      required version is <productname>Python</productname> 2.7.
-      <productname>Python 3</productname> is supported if it's
-      version 3.2 or later; but see
-      <xref linkend="plpython-python23"/>
-      when using Python 3.
+      required version is <productname>Python</productname> 3.2 or later.
      </para>
 
      <para>
@@ -1868,14 +1864,10 @@ build-postgresql:
        <term><envar>PYTHON</envar></term>
        <listitem>
         <para>
-         Python interpreter program.  This will be used to
-         determine the dependencies for building PL/Python.  Also,
-         whether Python 2 or 3 is specified here (or otherwise
-         implicitly chosen) determines which variant of the PL/Python
-         language becomes available.  See
-         <xref linkend="plpython-python23"/>
-         for more information.  If this is not set, the following are probed
-         in this order: <literal>python python3 python2</literal>.
+         Python interpreter program.  This will be used to determine the
+         dependencies for building PL/Python.  If this is not set, the
+         following are probed in this order:
+         <literal>python3 python</literal>.
         </para>
        </listitem>
       </varlistentry>
diff --git a/configure b/configure
index 93055556585..ba635a00622 100755
--- a/configure
+++ b/configure
@@ -10280,7 +10280,7 @@ fi
 
 if test "$with_python" = yes; then
   if test -z "$PYTHON"; then
-  for ac_prog in python python3 python2
+  for ac_prog in python3 python
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
@@ -10346,8 +10346,8 @@ python_majorversion=`echo "$python_fullversion" | sed 's/^\([0-9]*\).*/\1/'`
 python_minorversion=`echo "$python_fullversion" | sed 's/^[0-9]*\.\([0-9]*\).*/\1/'`
 python_version=`echo "$python_fullversion" | sed 's/^\([0-9]*\.[0-9]*\).*/\1/'`
 # Reject unsupported Python versions as soon as practical.
-if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 7; then
-  as_fn_error $? "Python version $python_version is too old (version 2.7 or later is required)" "$LINENO" 5
+if test "$python_majorversion" -lt 3; then
+  as_fn_error $? "Python version $python_version is too old (version 3 or later is required)" "$LINENO" 5
 fi
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python sysconfig module" >&5
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index bab81bd459a..105f5c72a2d 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -490,6 +490,10 @@ sub mkvcbuild
 		  if (!(defined($pyprefix) && defined($pyver)));
 
 		my $pymajorver = substr($pyver, 0, 1);
+
+		die "Python version $pyver is too old (version 3 or later is required)"
+		  if int($pymajorver) < 3;
+
 		my $plpython = $solution->AddProject('plpython' . $pymajorver,
 			'dll', 'PLs', 'src/pl/plpython');
 		$plpython->AddIncludeDir($pyprefix . '/include');
-- 
2.34.0

v5-0002-plpython-Remove-plpythonu-plpython2u-extensions.patchtext/x-diff; charset=us-asciiDownload
From 3720934d42ac1871284c33b85cfad781fb98be3f Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 15 Feb 2022 10:41:04 -0800
Subject: [PATCH v5 2/4] plpython: Remove plpythonu, plpython2u extensions.

Reviewed-By: Peter Eisentraut <peter@eisentraut.org> (in an earlier version)
---
 src/pl/plpython/plpython2u--1.0.sql | 17 -----------------
 src/pl/plpython/plpython2u.control  |  7 -------
 src/pl/plpython/plpythonu--1.0.sql  | 17 -----------------
 src/pl/plpython/plpythonu.control   |  7 -------
 contrib/hstore_plpython/Makefile    |  4 ++--
 contrib/jsonb_plpython/Makefile     |  4 ++--
 contrib/ltree_plpython/Makefile     |  4 ++--
 7 files changed, 6 insertions(+), 54 deletions(-)
 delete mode 100644 src/pl/plpython/plpython2u--1.0.sql
 delete mode 100644 src/pl/plpython/plpython2u.control
 delete mode 100644 src/pl/plpython/plpythonu--1.0.sql
 delete mode 100644 src/pl/plpython/plpythonu.control

diff --git a/src/pl/plpython/plpython2u--1.0.sql b/src/pl/plpython/plpython2u--1.0.sql
deleted file mode 100644
index 69f74775678..00000000000
--- a/src/pl/plpython/plpython2u--1.0.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/* src/pl/plpython/plpython2u--1.0.sql */
-
-CREATE FUNCTION plpython2_call_handler() RETURNS language_handler
-  LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython2_inline_handler(internal) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython2_validator(oid) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE LANGUAGE plpython2u
-  HANDLER plpython2_call_handler
-  INLINE plpython2_inline_handler
-  VALIDATOR plpython2_validator;
-
-COMMENT ON LANGUAGE plpython2u IS 'PL/Python2U untrusted procedural language';
diff --git a/src/pl/plpython/plpython2u.control b/src/pl/plpython/plpython2u.control
deleted file mode 100644
index 39c2b791efe..00000000000
--- a/src/pl/plpython/plpython2u.control
+++ /dev/null
@@ -1,7 +0,0 @@
-# plpython2u extension
-comment = 'PL/Python2U untrusted procedural language'
-default_version = '1.0'
-module_pathname = '$libdir/plpython2'
-relocatable = false
-schema = pg_catalog
-superuser = true
diff --git a/src/pl/plpython/plpythonu--1.0.sql b/src/pl/plpython/plpythonu--1.0.sql
deleted file mode 100644
index 4c6f7c3f140..00000000000
--- a/src/pl/plpython/plpythonu--1.0.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/* src/pl/plpython/plpythonu--1.0.sql */
-
-CREATE FUNCTION plpython_call_handler() RETURNS language_handler
-  LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_inline_handler(internal) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_validator(oid) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE LANGUAGE plpythonu
-  HANDLER plpython_call_handler
-  INLINE plpython_inline_handler
-  VALIDATOR plpython_validator;
-
-COMMENT ON LANGUAGE plpythonu IS 'PL/PythonU untrusted procedural language';
diff --git a/src/pl/plpython/plpythonu.control b/src/pl/plpython/plpythonu.control
deleted file mode 100644
index ae91b1c255c..00000000000
--- a/src/pl/plpython/plpythonu.control
+++ /dev/null
@@ -1,7 +0,0 @@
-# plpythonu extension
-comment = 'PL/PythonU untrusted procedural language'
-default_version = '1.0'
-module_pathname = '$libdir/plpython2'
-relocatable = false
-schema = pg_catalog
-superuser = true
diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile
index 6af097ae68b..19d99a8045e 100644
--- a/contrib/hstore_plpython/Makefile
+++ b/contrib/hstore_plpython/Makefile
@@ -6,8 +6,8 @@ OBJS = \
 	hstore_plpython.o
 PGFILEDESC = "hstore_plpython - hstore transform for plpython"
 
-EXTENSION = hstore_plpythonu hstore_plpython2u hstore_plpython3u
-DATA = hstore_plpythonu--1.0.sql hstore_plpython2u--1.0.sql hstore_plpython3u--1.0.sql
+EXTENSION = hstore_plpython3u
+DATA = hstore_plpython3u--1.0.sql
 
 REGRESS = hstore_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
diff --git a/contrib/jsonb_plpython/Makefile b/contrib/jsonb_plpython/Makefile
index ca767418943..ff1c1019f1f 100644
--- a/contrib/jsonb_plpython/Makefile
+++ b/contrib/jsonb_plpython/Makefile
@@ -8,8 +8,8 @@ PGFILEDESC = "jsonb_plpython - transform between jsonb and plpythonu"
 
 PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
-EXTENSION = jsonb_plpythonu jsonb_plpython2u jsonb_plpython3u
-DATA = jsonb_plpythonu--1.0.sql jsonb_plpython2u--1.0.sql jsonb_plpython3u--1.0.sql
+EXTENSION = jsonb_plpython3u
+DATA = jsonb_plpython3u--1.0.sql
 
 REGRESS = jsonb_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile
index 12a01467721..0bccb111e6b 100644
--- a/contrib/ltree_plpython/Makefile
+++ b/contrib/ltree_plpython/Makefile
@@ -6,8 +6,8 @@ OBJS = \
 	ltree_plpython.o
 PGFILEDESC = "ltree_plpython - ltree transform for plpython"
 
-EXTENSION = ltree_plpythonu ltree_plpython2u ltree_plpython3u
-DATA = ltree_plpythonu--1.0.sql ltree_plpython2u--1.0.sql ltree_plpython3u--1.0.sql
+EXTENSION = ltree_plpython3u
+DATA = ltree_plpython3u--1.0.sql
 
 REGRESS = ltree_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
-- 
2.34.0

v5-0003-plpython-Remove-regression-test-infrastructure-fo.patchtext/x-diff; charset=us-asciiDownload
From 39e6d8fdf08cd23295d1fba58056a83832ac4f07 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 15 Feb 2022 10:40:20 -0800
Subject: [PATCH v5 3/4] plpython: Remove regression test infrastructure for
 Python 2.

Reviewed-By: Peter Eisentraut <peter@eisentraut.org> (in an earlier version)
---
 src/pl/plpython/.gitignore                    |    2 -
 src/pl/plpython/Makefile                      |   14 -
 src/pl/plpython/expected/plpython_call.out    |   12 +-
 .../plpython/expected/plpython_composite.out  |   32 +-
 src/pl/plpython/expected/plpython_do.out      |    8 +-
 src/pl/plpython/expected/plpython_drop.out    |    3 +-
 src/pl/plpython/expected/plpython_ereport.out |   22 +-
 src/pl/plpython/expected/plpython_error.out   |   52 +-
 src/pl/plpython/expected/plpython_error_5.out |  447 --------
 src/pl/plpython/expected/plpython_global.out  |    6 +-
 src/pl/plpython/expected/plpython_import.out  |    8 +-
 src/pl/plpython/expected/plpython_newline.out |    6 +-
 src/pl/plpython/expected/plpython_params.out  |    8 +-
 src/pl/plpython/expected/plpython_quote.out   |    2 +-
 src/pl/plpython/expected/plpython_record.out  |   18 +-
 src/pl/plpython/expected/plpython_setof.out   |   18 +-
 src/pl/plpython/expected/plpython_spi.out     |   48 +-
 .../expected/plpython_subtransaction.out      |   38 +-
 src/pl/plpython/expected/plpython_test.out    |   12 +-
 .../expected/plpython_transaction.out         |   24 +-
 src/pl/plpython/expected/plpython_trigger.out |   46 +-
 src/pl/plpython/expected/plpython_types.out   |  230 ++--
 src/pl/plpython/expected/plpython_types_3.out | 1009 -----------------
 src/pl/plpython/expected/plpython_unicode.out |   16 +-
 src/pl/plpython/expected/plpython_void.out    |    6 +-
 src/pl/plpython/regress-python3-mangle.mk     |   38 -
 src/pl/plpython/sql/plpython_call.sql         |   12 +-
 src/pl/plpython/sql/plpython_composite.sql    |   32 +-
 src/pl/plpython/sql/plpython_do.sql           |    6 +-
 src/pl/plpython/sql/plpython_drop.sql         |    4 +-
 src/pl/plpython/sql/plpython_ereport.sql      |   22 +-
 src/pl/plpython/sql/plpython_error.sql        |   48 +-
 src/pl/plpython/sql/plpython_global.sql       |    6 +-
 src/pl/plpython/sql/plpython_import.sql       |    8 +-
 src/pl/plpython/sql/plpython_newline.sql      |    6 +-
 src/pl/plpython/sql/plpython_params.sql       |    8 +-
 src/pl/plpython/sql/plpython_quote.sql        |    2 +-
 src/pl/plpython/sql/plpython_record.sql       |   18 +-
 src/pl/plpython/sql/plpython_setof.sql        |   18 +-
 src/pl/plpython/sql/plpython_spi.sql          |   48 +-
 .../plpython/sql/plpython_subtransaction.sql  |   38 +-
 src/pl/plpython/sql/plpython_test.sql         |   12 +-
 src/pl/plpython/sql/plpython_transaction.sql  |   22 +-
 src/pl/plpython/sql/plpython_trigger.sql      |   46 +-
 src/pl/plpython/sql/plpython_types.sql        |  106 +-
 src/pl/plpython/sql/plpython_unicode.sql      |   16 +-
 src/pl/plpython/sql/plpython_void.sql         |    6 +-
 contrib/hstore_plpython/.gitignore            |    2 -
 contrib/hstore_plpython/Makefile              |    6 -
 .../expected/hstore_plpython.out              |   22 +-
 .../hstore_plpython2u--1.0.sql                |   19 -
 .../hstore_plpython/hstore_plpython2u.control |    6 -
 .../hstore_plpython/hstore_plpythonu--1.0.sql |   19 -
 .../hstore_plpython/hstore_plpythonu.control  |    6 -
 .../hstore_plpython/sql/hstore_plpython.sql   |   18 +-
 contrib/jsonb_plpython/.gitignore             |    2 -
 contrib/jsonb_plpython/Makefile               |    7 -
 .../expected/jsonb_plpython.out               |   32 +-
 .../jsonb_plpython/jsonb_plpython2u--1.0.sql  |   19 -
 .../jsonb_plpython/jsonb_plpython2u.control   |    6 -
 .../jsonb_plpython/jsonb_plpythonu--1.0.sql   |   19 -
 .../jsonb_plpython/jsonb_plpythonu.control    |    6 -
 contrib/jsonb_plpython/sql/jsonb_plpython.sql |   30 +-
 contrib/ltree_plpython/.gitignore             |    2 -
 contrib/ltree_plpython/Makefile               |    6 -
 .../expected/ltree_plpython.out               |   10 +-
 .../ltree_plpython/ltree_plpython2u--1.0.sql  |   12 -
 .../ltree_plpython/ltree_plpython2u.control   |    6 -
 .../ltree_plpython/ltree_plpythonu--1.0.sql   |   12 -
 .../ltree_plpython/ltree_plpythonu.control    |    6 -
 contrib/ltree_plpython/sql/ltree_plpython.sql |    8 +-
 src/tools/msvc/vcregress.pl                   |   76 +-
 72 files changed, 607 insertions(+), 2359 deletions(-)
 delete mode 100644 src/pl/plpython/expected/plpython_error_5.out
 delete mode 100644 src/pl/plpython/expected/plpython_types_3.out
 delete mode 100644 src/pl/plpython/regress-python3-mangle.mk
 delete mode 100644 contrib/hstore_plpython/hstore_plpython2u--1.0.sql
 delete mode 100644 contrib/hstore_plpython/hstore_plpython2u.control
 delete mode 100644 contrib/hstore_plpython/hstore_plpythonu--1.0.sql
 delete mode 100644 contrib/hstore_plpython/hstore_plpythonu.control
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpython2u.control
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpythonu.control
 delete mode 100644 contrib/ltree_plpython/ltree_plpython2u--1.0.sql
 delete mode 100644 contrib/ltree_plpython/ltree_plpython2u.control
 delete mode 100644 contrib/ltree_plpython/ltree_plpythonu--1.0.sql
 delete mode 100644 contrib/ltree_plpython/ltree_plpythonu.control

diff --git a/src/pl/plpython/.gitignore b/src/pl/plpython/.gitignore
index 70c08db3231..07bee6a29c4 100644
--- a/src/pl/plpython/.gitignore
+++ b/src/pl/plpython/.gitignore
@@ -1,7 +1,5 @@
 /spiexceptions.h
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index a83ae8865c7..0d6f74de71f 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -35,9 +35,6 @@ OBJS = \
 	plpy_util.o
 
 DATA = $(NAME)u.control $(NAME)u--1.0.sql
-ifeq ($(python_majorversion),2)
-DATA += plpythonu.control plpythonu--1.0.sql
-endif
 
 # header files to install - it's not clear which of these might be needed
 # so install them all.
@@ -77,11 +74,6 @@ endif # win32
 SHLIB_LINK = $(python_libspec) $(python_additional_libs) $(filter -lintl,$(LIBS))
 
 REGRESS_OPTS = --dbname=$(PL_TESTDB)
-# Only load plpythonu with Python 2.  The test files themselves load
-# the versioned language plpython(2|3)u.
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu
-endif
 
 REGRESS = \
 	plpython_schema \
@@ -108,8 +100,6 @@ REGRESS = \
 	plpython_transaction \
 	plpython_drop
 
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
-
 include $(top_srcdir)/src/Makefile.shlib
 
 all: all-lib
@@ -127,7 +117,6 @@ uninstall: uninstall-lib uninstall-data
 install-data: installdirs
 	$(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA)) '$(DESTDIR)$(datadir)/extension/'
 	$(INSTALL_DATA) $(addprefix $(srcdir)/, $(INCS)) '$(DESTDIR)$(includedir_server)'
-	$(INSTALL_DATA) $(srcdir)/regress-python3-mangle.mk '$(DESTDIR)$(pgxsdir)/src/pl/plpython'
 
 uninstall-data:
 	rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
@@ -136,9 +125,6 @@ uninstall-data:
 .PHONY: install-data uninstall-data
 
 
-include $(srcdir)/regress-python3-mangle.mk
-
-
 check: submake-pg-regress
 	$(pg_regress_check) $(REGRESS_OPTS) $(REGRESS)
 
diff --git a/src/pl/plpython/expected/plpython_call.out b/src/pl/plpython/expected/plpython_call.out
index 55e1027246a..4c0690067a0 100644
--- a/src/pl/plpython/expected/plpython_call.out
+++ b/src/pl/plpython/expected/plpython_call.out
@@ -2,14 +2,14 @@
 -- Tests for procedures / CALL syntax
 --
 CREATE PROCEDURE test_proc1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 pass
 $$;
 CALL test_proc1();
 -- error: can't return non-None
 CREATE PROCEDURE test_proc2()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return 5
 $$;
@@ -18,7 +18,7 @@ ERROR:  PL/Python procedure did not return None
 CONTEXT:  PL/Python procedure "test_proc2"
 CREATE TABLE test1 (a int);
 CREATE PROCEDURE test_proc3(x int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("INSERT INTO test1 VALUES (%s)" % x)
 $$;
@@ -31,7 +31,7 @@ SELECT * FROM test1;
 
 -- output arguments
 CREATE PROCEDURE test_proc5(INOUT a text)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return [a + '+' + a]
 $$;
@@ -42,7 +42,7 @@ CALL test_proc5('abc');
 (1 row)
 
 CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return (b * a, c * a)
 $$;
@@ -54,7 +54,7 @@ CALL test_proc6(2, 3, 4);
 
 -- OUT parameters
 CREATE PROCEDURE test_proc9(IN a int, OUT b int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.notice("a: %s" % (a))
 return (a * 2,)
diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out
index af801923343..bb101e07d53 100644
--- a/src/pl/plpython/expected/plpython_composite.out
+++ b/src/pl/plpython/expected/plpython_composite.out
@@ -1,6 +1,6 @@
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT multiout_simple();
  multiout_simple 
 -----------------
@@ -27,7 +27,7 @@ SELECT (multiout_simple()).j + 3;
 
 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT multiout_simple_setof();
  multiout_simple_setof 
 -----------------------
@@ -67,7 +67,7 @@ elif typ == 'obj':
     return type_record
 elif typ == 'str':
     return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
  first | second 
 -------+--------
@@ -237,7 +237,7 @@ for i in range(n):
     power = 2 ** i
     length = plpy.execute("select length('%d')" % power)[0]['length']
     yield power, length
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_setof(3);
  power_of_2 | length 
 ------------+--------
@@ -260,7 +260,7 @@ CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
 return [{'x': 4, 'y' :'four'},
         {'x': 7, 'y' :'seven'},
         {'x': 0, 'y' :'zero'}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_return_table();
  x |   y   
 ---+-------
@@ -273,7 +273,7 @@ CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS
 yield [[1], 'a']
 yield [[1,2], 'b']
 yield [[1,2,3], None]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_array();
  column1 | column2 
 ---------+---------
@@ -284,11 +284,11 @@ SELECT * FROM multiout_array();
 
 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
 return {'first': 1, 'second': 2}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
 return [{'first': 1, 'second': 2},
        {'first': 3, 'second': 4	}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM singleout_composite();
  first | second 
 -------+--------
@@ -305,7 +305,7 @@ SELECT * FROM multiout_composite();
 -- composite OUT parameters in functions returning RECORD not supported yet
 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
 return (n, (n * 2, n * 3))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
 if returnnull:
     d = None
@@ -323,7 +323,7 @@ elif typ == 'str':
     d = "(%r,%r)" % (n * 2, n * 3)
 for i in range(n):
     yield (i, d)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_composite(2);
  n | column2 
 ---+---------
@@ -438,7 +438,7 @@ CREATE TABLE changing (
 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
 return [(1, {'i': 1, 'j': 2}),
         (1, (3, 4))]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM changing_test();
  n | column2 
 ---+---------
@@ -474,7 +474,7 @@ yield {'tab': [('first', 1), ('second', 2)],
 yield {'tab': [('first', 1), ('second', 2)],
       'typ': [{'first': 'third', 'second': 3},
               {'first': 'fourth', 'second': 4}]}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_types_table();
             tab             |            typ             
 ----------------------------+----------------------------
@@ -486,7 +486,7 @@ SELECT * FROM composite_types_table();
 -- check what happens if the output record descriptor changes
 CREATE FUNCTION return_record(t text) RETURNS record AS $$
 return {'t': t, 'val': 10}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM return_record('abc') AS r(t text, val integer);
   t  | val 
 -----+-----
@@ -525,7 +525,7 @@ SELECT * FROM return_record('999') AS r(val text, t integer);
 
 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
 return {'v1':1,'v2':2,t:3}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
  v3 | v2 | v1 
 ----+----+----
@@ -572,7 +572,7 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
 -- multi-dimensional array of composite types.
 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list();
                                composite_type_as_list                               
 ------------------------------------------------------------------------------------
@@ -585,7 +585,7 @@ SELECT * FROM composite_type_as_list();
 -- on the issue.
 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
   return [['first', 1]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list_broken();
 ERROR:  malformed record literal: "first"
 DETAIL:  Missing left parenthesis.
diff --git a/src/pl/plpython/expected/plpython_do.out b/src/pl/plpython/expected/plpython_do.out
index e300530e031..d131a4c0ed6 100644
--- a/src/pl/plpython/expected/plpython_do.out
+++ b/src/pl/plpython/expected/plpython_do.out
@@ -1,8 +1,6 @@
-DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
-NOTICE:  This is plpythonu.
-DO $$ plpy.notice("This is plpython2u.") $$ LANGUAGE plpython2u;
-NOTICE:  This is plpython2u.
-DO $$ raise Exception("error test") $$ LANGUAGE plpythonu;
+DO $$ plpy.notice("This is plpython3u.") $$ LANGUAGE plpython3u;
+NOTICE:  This is plpython3u.
+DO $$ raise Exception("error test") $$ LANGUAGE plpython3u;
 ERROR:  Exception: error test
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
diff --git a/src/pl/plpython/expected/plpython_drop.out b/src/pl/plpython/expected/plpython_drop.out
index a0e3b5c4ef6..97bb54a55e7 100644
--- a/src/pl/plpython/expected/plpython_drop.out
+++ b/src/pl/plpython/expected/plpython_drop.out
@@ -2,5 +2,4 @@
 -- For paranoia's sake, don't leave an untrusted language sitting around
 --
 SET client_min_messages = WARNING;
-DROP EXTENSION plpythonu CASCADE;
-DROP EXTENSION IF EXISTS plpython2u CASCADE;
+DROP EXTENSION plpython3u CASCADE;
diff --git a/src/pl/plpython/expected/plpython_ereport.out b/src/pl/plpython/expected/plpython_ereport.out
index b73bfff5115..b38bb91e894 100644
--- a/src/pl/plpython/expected/plpython_ereport.out
+++ b/src/pl/plpython/expected/plpython_ereport.out
@@ -17,7 +17,7 @@ plpy.info('This is message text.',
 plpy.notice('notice', detail='some detail')
 plpy.warning('warning', detail='some detail')
 plpy.error('stop on error', detail='some detail', hint='some hint')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT elog_test();
 INFO:  info
 DETAIL:  some detail
@@ -38,42 +38,42 @@ CONTEXT:  Traceback (most recent call last):
   PL/Python function "elog_test", line 18, in <module>
     plpy.error('stop on error', detail='some detail', hint='some hint')
 PL/Python function "elog_test"
-DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpythonu;
+DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  (10, 20)
 DO $$
 import time;
 from datetime import date
 plpy.info('other types', detail=date(2016, 2, 26))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  2016-02-26
 DO $$
 basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 plpy.info('other types', detail=basket)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 -- should fail
-DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u;
 ERROR:  ValueError: invalid SQLSTATE code
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('wrong sqlstate', sqlstate='54444A') 
 PL/Python anonymous code block
-DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: 'blabla' is an invalid keyword argument for this function
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('unsupported argument', blabla='fooboo') 
 PL/Python anonymous code block
-DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: argument 'message' given by name and position
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('first message', message='second message') 
 PL/Python anonymous code block
-DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: argument 'message' given by name and position
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
@@ -96,7 +96,7 @@ kwargs = {
 }
 # ignore None values
 plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT raise_exception('hello', 'world');
 ERROR:  plpy.Error: hello
 DETAIL:  world
@@ -189,7 +189,7 @@ try:
 except Exception as e:
     plpy.info(e.spidata)
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  (119577128, None, 'some hint', None, 0, None, 'users_tab', None, 'user_type', None)
 ERROR:  plpy.SPIError: plpy.Error: my message
 HINT:  some hint
@@ -199,7 +199,7 @@ try:
 except Exception as e:
     plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name))
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  sqlstate: XX987, hint: some hint, table_name: users_tab, datatype_name: user_type
 ERROR:  plpy.Error: my message
 HINT:  some hint
diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index b2f8fe83eb6..7fe864a1a57 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -6,7 +6,7 @@
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 ERROR:  could not compile PL/Python function "python_syntax_error"
 DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
 /* With check_function_bodies = false the function should get defined
@@ -16,7 +16,7 @@ SET check_function_bodies = false;
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 SELECT python_syntax_error();
 ERROR:  could not compile PL/Python function "python_syntax_error"
 DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
@@ -30,7 +30,7 @@ RESET check_function_bodies;
 CREATE FUNCTION sql_syntax_error() RETURNS text
         AS
 'plpy.execute("syntax error")'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 SELECT sql_syntax_error();
 ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
 LINE 1: syntax error
@@ -45,7 +45,7 @@ PL/Python function "sql_syntax_error"
 CREATE FUNCTION exception_index_invalid(text) RETURNS text
 	AS
 'return args[1]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT exception_index_invalid('test');
 ERROR:  IndexError: list index out of range
 CONTEXT:  Traceback (most recent call last):
@@ -58,7 +58,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text
 	AS
 'rv = plpy.execute("SELECT test5(''foo'')")
 return rv[0]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT exception_index_invalid_nested();
 ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
 LINE 1: SELECT test5('foo')
@@ -81,7 +81,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_uncaught('rick');
 ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
 CONTEXT:  Traceback (most recent call last):
@@ -105,7 +105,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_caught('rick');
 NOTICE:  type "test" does not exist
  invalid_type_caught 
@@ -129,7 +129,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_reraised('rick');
 ERROR:  plpy.Error: type "test" does not exist
 CONTEXT:  Traceback (most recent call last):
@@ -147,7 +147,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT valid_type('rick');
  valid_type 
 ------------
@@ -170,7 +170,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_error();
 ERROR:  plpy.Error: boom
 CONTEXT:  Traceback (most recent call last):
@@ -199,7 +199,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_error_raise();
 ERROR:  plpy.Error: boom
 CONTEXT:  Traceback (most recent call last):
@@ -228,7 +228,7 @@ def fun3():
 fun3()
 return "you''ve been warned"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_warning();
 WARNING:  boom
    nested_warning   
@@ -241,9 +241,9 @@ WARNING:  boom
 CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
 $$
 plpy.nonexistent
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT toplevel_attribute_error();
-ERROR:  AttributeError: 'module' object has no attribute 'nonexistent'
+ERROR:  AttributeError: module 'plpy' has no attribute 'nonexistent'
 CONTEXT:  Traceback (most recent call last):
   PL/Python function "toplevel_attribute_error", line 2, in <module>
     plpy.nonexistent
@@ -261,7 +261,7 @@ def third():
   plpy.execute("select sql_error()")
 
 first()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
 begin
   select 1/0;
@@ -274,7 +274,7 @@ end
 $$ LANGUAGE plpgsql;
 CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
 plpy.execute("select sql_error()")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT python_traceback();
 ERROR:  spiexceptions.DivisionByZero: division by zero
 CONTEXT:  Traceback (most recent call last):
@@ -325,7 +325,7 @@ except spiexceptions.NotNullViolation as e:
     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
 except spiexceptions.UniqueViolation as e:
     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT specific_exception(2);
  specific_exception 
 --------------------
@@ -351,7 +351,7 @@ NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
 CREATE FUNCTION python_unique_violation() RETURNS void AS $$
 plpy.execute("insert into specific values (1)")
 plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
 begin
     begin
@@ -374,7 +374,7 @@ CREATE FUNCTION manual_subxact() RETURNS void AS $$
 plpy.execute("savepoint save")
 plpy.execute("create table foo(x integer)")
 plpy.execute("rollback to save")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT manual_subxact();
 ERROR:  plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
 CONTEXT:  Traceback (most recent call last):
@@ -389,7 +389,7 @@ rollback = plpy.prepare("rollback to save")
 plpy.execute(save)
 plpy.execute("create table foo(x integer)")
 plpy.execute(rollback)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT manual_subxact_prepared();
 ERROR:  plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
 CONTEXT:  Traceback (most recent call last):
@@ -400,7 +400,7 @@ PL/Python function "manual_subxact_prepared"
  */
 CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
 raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 DO $$
 BEGIN
 	SELECT plpy_raise_spiexception();
@@ -414,7 +414,7 @@ CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
 exc = plpy.spiexceptions.DivisionByZero()
 exc.sqlstate = 'SILLY'
 raise exc
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 DO $$
 BEGIN
 	SELECT plpy_raise_spiexception_override();
@@ -425,18 +425,18 @@ $$ LANGUAGE plpgsql;
 /* test the context stack trace for nested execution levels
  */
 CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
 plpy.execute("SELECT notice_innerfunc()")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 \set SHOW_CONTEXT always
 SELECT notice_outerfunc();
 NOTICE:  inside DO
 CONTEXT:  PL/Python anonymous code block
-SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
+SQL statement "DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$"
 PL/Python function "notice_innerfunc"
 SQL statement "SELECT notice_innerfunc()"
 PL/Python function "notice_outerfunc"
diff --git a/src/pl/plpython/expected/plpython_error_5.out b/src/pl/plpython/expected/plpython_error_5.out
deleted file mode 100644
index bc66ab55340..00000000000
--- a/src/pl/plpython/expected/plpython_error_5.out
+++ /dev/null
@@ -1,447 +0,0 @@
--- test error handling, i forgot to restore Warn_restart in
--- the trigger handler once. the errors and subsequent core dump were
--- interesting.
-/* Flat out Python syntax error
- */
-CREATE FUNCTION python_syntax_error() RETURNS text
-        AS
-'.syntaxerror'
-        LANGUAGE plpython3u;
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-/* With check_function_bodies = false the function should get defined
- * and the error reported when called
- */
-SET check_function_bodies = false;
-CREATE FUNCTION python_syntax_error() RETURNS text
-        AS
-'.syntaxerror'
-        LANGUAGE plpython3u;
-SELECT python_syntax_error();
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-/* Run the function twice to check if the hashtable entry gets cleaned up */
-SELECT python_syntax_error();
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-RESET check_function_bodies;
-/* Flat out syntax error
- */
-CREATE FUNCTION sql_syntax_error() RETURNS text
-        AS
-'plpy.execute("syntax error")'
-        LANGUAGE plpython3u;
-SELECT sql_syntax_error();
-ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
-LINE 1: syntax error
-        ^
-QUERY:  syntax error
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "sql_syntax_error", line 1, in <module>
-    plpy.execute("syntax error")
-PL/Python function "sql_syntax_error"
-/* check the handling of uncaught python exceptions
- */
-CREATE FUNCTION exception_index_invalid(text) RETURNS text
-	AS
-'return args[1]'
-	LANGUAGE plpython3u;
-SELECT exception_index_invalid('test');
-ERROR:  IndexError: list index out of range
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "exception_index_invalid", line 1, in <module>
-    return args[1]
-PL/Python function "exception_index_invalid"
-/* check handling of nested exceptions
- */
-CREATE FUNCTION exception_index_invalid_nested() RETURNS text
-	AS
-'rv = plpy.execute("SELECT test5(''foo'')")
-return rv[0]'
-	LANGUAGE plpython3u;
-SELECT exception_index_invalid_nested();
-ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
-LINE 1: SELECT test5('foo')
-               ^
-HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
-QUERY:  SELECT test5('foo')
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "exception_index_invalid_nested", line 1, in <module>
-    rv = plpy.execute("SELECT test5('foo')")
-PL/Python function "exception_index_invalid_nested"
-/* a typo
- */
-CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	SD["plan"] = plpy.prepare(q, [ "test" ])
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_uncaught('rick');
-ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "invalid_type_uncaught", line 3, in <module>
-    SD["plan"] = plpy.prepare(q, [ "test" ])
-PL/Python function "invalid_type_uncaught"
-/* for what it's worth catch the exception generated by
- * the typo, and return None
- */
-CREATE FUNCTION invalid_type_caught(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	try:
-		SD["plan"] = plpy.prepare(q, [ "test" ])
-	except plpy.SPIError as ex:
-		plpy.notice(str(ex))
-		return None
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_caught('rick');
-NOTICE:  type "test" does not exist
- invalid_type_caught 
----------------------
- 
-(1 row)
-
-/* for what it's worth catch the exception generated by
- * the typo, and reraise it as a plain error
- */
-CREATE FUNCTION invalid_type_reraised(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	try:
-		SD["plan"] = plpy.prepare(q, [ "test" ])
-	except plpy.SPIError as ex:
-		plpy.error(str(ex))
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_reraised('rick');
-ERROR:  plpy.Error: type "test" does not exist
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "invalid_type_reraised", line 6, in <module>
-    plpy.error(str(ex))
-PL/Python function "invalid_type_reraised"
-/* no typo no messing about
- */
-CREATE FUNCTION valid_type(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ])
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT valid_type('rick');
- valid_type 
-------------
- 
-(1 row)
-
-/* error in nested functions to get a traceback
-*/
-CREATE FUNCTION nested_error() RETURNS text
-	AS
-'def fun1():
-	plpy.error("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "not reached"
-'
-	LANGUAGE plpython3u;
-SELECT nested_error();
-ERROR:  plpy.Error: boom
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "nested_error", line 10, in <module>
-    fun3()
-  PL/Python function "nested_error", line 8, in fun3
-    fun2()
-  PL/Python function "nested_error", line 5, in fun2
-    fun1()
-  PL/Python function "nested_error", line 2, in fun1
-    plpy.error("boom")
-PL/Python function "nested_error"
-/* raising plpy.Error is just like calling plpy.error
-*/
-CREATE FUNCTION nested_error_raise() RETURNS text
-	AS
-'def fun1():
-	raise plpy.Error("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "not reached"
-'
-	LANGUAGE plpython3u;
-SELECT nested_error_raise();
-ERROR:  plpy.Error: boom
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "nested_error_raise", line 10, in <module>
-    fun3()
-  PL/Python function "nested_error_raise", line 8, in fun3
-    fun2()
-  PL/Python function "nested_error_raise", line 5, in fun2
-    fun1()
-  PL/Python function "nested_error_raise", line 2, in fun1
-    raise plpy.Error("boom")
-PL/Python function "nested_error_raise"
-/* using plpy.warning should not produce a traceback
-*/
-CREATE FUNCTION nested_warning() RETURNS text
-	AS
-'def fun1():
-	plpy.warning("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "you''ve been warned"
-'
-	LANGUAGE plpython3u;
-SELECT nested_warning();
-WARNING:  boom
-   nested_warning   
---------------------
- you've been warned
-(1 row)
-
-/* AttributeError at toplevel used to give segfaults with the traceback
-*/
-CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
-$$
-plpy.nonexistent
-$$ LANGUAGE plpython3u;
-SELECT toplevel_attribute_error();
-ERROR:  AttributeError: module 'plpy' has no attribute 'nonexistent'
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "toplevel_attribute_error", line 2, in <module>
-    plpy.nonexistent
-PL/Python function "toplevel_attribute_error"
-/* Calling PL/Python functions from SQL and vice versa should not lose context.
- */
-CREATE OR REPLACE FUNCTION python_traceback() RETURNS void AS $$
-def first():
-  second()
-
-def second():
-  third()
-
-def third():
-  plpy.execute("select sql_error()")
-
-first()
-$$ LANGUAGE plpython3u;
-CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
-begin
-  select 1/0;
-end
-$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION python_from_sql_error() RETURNS void AS $$
-begin
-  select python_traceback();
-end
-$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
-plpy.execute("select sql_error()")
-$$ LANGUAGE plpython3u;
-SELECT python_traceback();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "python_traceback", line 11, in <module>
-    first()
-  PL/Python function "python_traceback", line 3, in first
-    second()
-  PL/Python function "python_traceback", line 6, in second
-    third()
-  PL/Python function "python_traceback", line 9, in third
-    plpy.execute("select sql_error()")
-PL/Python function "python_traceback"
-SELECT sql_error();
-ERROR:  division by zero
-CONTEXT:  SQL statement "select 1/0"
-PL/pgSQL function sql_error() line 3 at SQL statement
-SELECT python_from_sql_error();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "python_traceback", line 11, in <module>
-    first()
-  PL/Python function "python_traceback", line 3, in first
-    second()
-  PL/Python function "python_traceback", line 6, in second
-    third()
-  PL/Python function "python_traceback", line 9, in third
-    plpy.execute("select sql_error()")
-PL/Python function "python_traceback"
-SQL statement "select python_traceback()"
-PL/pgSQL function python_from_sql_error() line 3 at SQL statement
-SELECT sql_from_python_error();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "sql_from_python_error", line 2, in <module>
-    plpy.execute("select sql_error()")
-PL/Python function "sql_from_python_error"
-/* check catching specific types of exceptions
- */
-CREATE TABLE specific (
-    i integer PRIMARY KEY
-);
-CREATE FUNCTION specific_exception(i integer) RETURNS void AS
-$$
-from plpy import spiexceptions
-try:
-    plpy.execute("insert into specific values (%s)" % (i or "NULL"));
-except spiexceptions.NotNullViolation as e:
-    plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
-except spiexceptions.UniqueViolation as e:
-    plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpython3u;
-SELECT specific_exception(2);
- specific_exception 
---------------------
- 
-(1 row)
-
-SELECT specific_exception(NULL);
-NOTICE:  Violated the NOT NULL constraint, sqlstate 23502
- specific_exception 
---------------------
- 
-(1 row)
-
-SELECT specific_exception(2);
-NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
- specific_exception 
---------------------
- 
-(1 row)
-
-/* SPI errors in PL/Python functions should preserve the SQLSTATE value
- */
-CREATE FUNCTION python_unique_violation() RETURNS void AS $$
-plpy.execute("insert into specific values (1)")
-plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpython3u;
-CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
-begin
-    begin
-        perform python_unique_violation();
-    exception when unique_violation then
-        return 'ok';
-    end;
-    return 'not reached';
-end;
-$$ language plpgsql;
-SELECT catch_python_unique_violation();
- catch_python_unique_violation 
--------------------------------
- ok
-(1 row)
-
-/* manually starting subtransactions - a bad idea
- */
-CREATE FUNCTION manual_subxact() RETURNS void AS $$
-plpy.execute("savepoint save")
-plpy.execute("create table foo(x integer)")
-plpy.execute("rollback to save")
-$$ LANGUAGE plpython3u;
-SELECT manual_subxact();
-ERROR:  plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "manual_subxact", line 2, in <module>
-    plpy.execute("savepoint save")
-PL/Python function "manual_subxact"
-/* same for prepared plans
- */
-CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
-save = plpy.prepare("savepoint save")
-rollback = plpy.prepare("rollback to save")
-plpy.execute(save)
-plpy.execute("create table foo(x integer)")
-plpy.execute(rollback)
-$$ LANGUAGE plpython3u;
-SELECT manual_subxact_prepared();
-ERROR:  plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "manual_subxact_prepared", line 4, in <module>
-    plpy.execute(save)
-PL/Python function "manual_subxact_prepared"
-/* raising plpy.spiexception.* from python code should preserve sqlstate
- */
-CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
-raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpython3u;
-DO $$
-BEGIN
-	SELECT plpy_raise_spiexception();
-EXCEPTION WHEN division_by_zero THEN
-	-- NOOP
-END
-$$ LANGUAGE plpgsql;
-/* setting a custom sqlstate should be handled
- */
-CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
-exc = plpy.spiexceptions.DivisionByZero()
-exc.sqlstate = 'SILLY'
-raise exc
-$$ LANGUAGE plpython3u;
-DO $$
-BEGIN
-	SELECT plpy_raise_spiexception_override();
-EXCEPTION WHEN SQLSTATE 'SILLY' THEN
-	-- NOOP
-END
-$$ LANGUAGE plpgsql;
-/* test the context stack trace for nested execution levels
- */
-CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
-return 1
-$$ LANGUAGE plpythonu;
-CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
-plpy.execute("SELECT notice_innerfunc()")
-return 1
-$$ LANGUAGE plpythonu;
-\set SHOW_CONTEXT always
-SELECT notice_outerfunc();
-NOTICE:  inside DO
-CONTEXT:  PL/Python anonymous code block
-SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
-PL/Python function "notice_innerfunc"
-SQL statement "SELECT notice_innerfunc()"
-PL/Python function "notice_outerfunc"
- notice_outerfunc 
-------------------
-                1
-(1 row)
-
diff --git a/src/pl/plpython/expected/plpython_global.out b/src/pl/plpython/expected/plpython_global.out
index 192e3e48a72..a4cfb1483f9 100644
--- a/src/pl/plpython/expected/plpython_global.out
+++ b/src/pl/plpython/expected/plpython_global.out
@@ -8,7 +8,7 @@ CREATE FUNCTION global_test_one() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_one"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION global_test_two() returns text
     AS
 'if "global_test" not in SD:
@@ -16,7 +16,7 @@ CREATE FUNCTION global_test_two() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_two"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION static_test() returns int4
     AS
 'if "call" in SD:
@@ -25,7 +25,7 @@ else:
 	SD["call"] = 1
 return SD["call"]
 '
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 SELECT static_test();
  static_test 
 -------------
diff --git a/src/pl/plpython/expected/plpython_import.out b/src/pl/plpython/expected/plpython_import.out
index b59e1821a79..854e989eaf9 100644
--- a/src/pl/plpython/expected/plpython_import.out
+++ b/src/pl/plpython/expected/plpython_import.out
@@ -6,7 +6,7 @@ CREATE FUNCTION import_fail() returns text
 except ImportError:
 	return "failed as expected"
 return "succeeded, that wasn''t supposed to happen"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION import_succeed() returns text
 	AS
 'try:
@@ -25,7 +25,7 @@ except Exception as ex:
 	plpy.notice("import failed -- %s" % str(ex))
 	return "failed, that wasn''t supposed to happen"
 return "succeeded, as expected"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION import_test_one(p text) RETURNS text
 	AS
 'try:
@@ -35,7 +35,7 @@ except ImportError:
     import sha
     digest = sha.new(p)
 return digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION import_test_two(u users) RETURNS text
 	AS
 'plain = u["fname"] + u["lname"]
@@ -46,7 +46,7 @@ except ImportError:
     import sha
     digest = sha.new(plain);
 return "sha hash of " + plain + " is " + digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 -- import python modules
 --
 SELECT import_fail();
diff --git a/src/pl/plpython/expected/plpython_newline.out b/src/pl/plpython/expected/plpython_newline.out
index 27dc2f8ab0c..2bc149257e7 100644
--- a/src/pl/plpython/expected/plpython_newline.out
+++ b/src/pl/plpython/expected/plpython_newline.out
@@ -3,13 +3,13 @@
 --
 CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS
 E'x = 100\ny = 23\nreturn x + y\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS
 E'x = 100\ry = 23\rreturn x + y\r'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS
 E'x = 100\r\ny = 23\r\nreturn x + y\r\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 SELECT newline_lf();
  newline_lf 
 ------------
diff --git a/src/pl/plpython/expected/plpython_params.out b/src/pl/plpython/expected/plpython_params.out
index 46ea7dfb90b..d1a36f36239 100644
--- a/src/pl/plpython/expected/plpython_params.out
+++ b/src/pl/plpython/expected/plpython_params.out
@@ -3,12 +3,12 @@
 --
 CREATE FUNCTION test_param_names0(integer, integer) RETURNS int AS $$
 return args[0] + args[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_param_names1(a0 integer, a1 text) RETURNS boolean AS $$
 assert a0 == args[0]
 assert a1 == args[1]
 return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_param_names2(u users) RETURNS text AS $$
 assert u == args[0]
 if isinstance(u, dict):
@@ -19,7 +19,7 @@ if isinstance(u, dict):
 else:
     s = str(u)
 return s
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- use deliberately wrong parameter names
 CREATE FUNCTION test_param_names3(a0 integer) RETURNS boolean AS $$
 try:
@@ -28,7 +28,7 @@ try:
 except NameError as e:
 	assert e.args[0].find("a1") > -1
 	return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_param_names0(2,7);
  test_param_names0 
 -------------------
diff --git a/src/pl/plpython/expected/plpython_quote.out b/src/pl/plpython/expected/plpython_quote.out
index eed72923aec..1fbe93d5351 100644
--- a/src/pl/plpython/expected/plpython_quote.out
+++ b/src/pl/plpython/expected/plpython_quote.out
@@ -8,7 +8,7 @@ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
         return plpy.quote_ident(t)
     else:
         raise plpy.Error("unrecognized quote type %s" % how)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT quote(t, 'literal') FROM (VALUES
        ('abc'),
        ('a''bc'),
diff --git a/src/pl/plpython/expected/plpython_record.out b/src/pl/plpython/expected/plpython_record.out
index 458330713a8..31de198582b 100644
--- a/src/pl/plpython/expected/plpython_record.out
+++ b/src/pl/plpython/expected/plpython_record.out
@@ -23,7 +23,7 @@ elif typ == 'obj':
 	type_record.first = first
 	type_record.second = second
 	return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
 if retnull:
 	return None
@@ -40,17 +40,17 @@ elif typ == 'obj':
 	return type_record
 elif typ == 'str':
 	return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
 return first + '_in_to_out';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- Test tuple returning functions
 SELECT * FROM test_table_record_as('dict', null, null, false);
  first | second 
@@ -340,7 +340,7 @@ SELECT * FROM test_type_record_as('obj', 'one', 1, false);
 -- errors cases
 CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
     return { 'first': 'first' }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error1();
 ERROR:  key "second" not found in mapping
 HINT:  To return null in a column, add the value None to the mapping with the key named after the column.
@@ -348,7 +348,7 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_record_error1"
 CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
     return [ 'first' ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error2();
 ERROR:  length of returned sequence did not match number of columns in row
 CONTEXT:  while creating return value
@@ -357,7 +357,7 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
     class type_record: pass
     type_record.first = 'first'
     return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error3();
 ERROR:  attribute "second" does not exist in Python object
 HINT:  To return null in a column, let the returned object have an attribute named after column with value None.
@@ -365,7 +365,7 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_record_error3"
 CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
     return 'foo'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error4();
 ERROR:  malformed record literal: "foo"
 DETAIL:  Missing left parenthesis.
diff --git a/src/pl/plpython/expected/plpython_setof.out b/src/pl/plpython/expected/plpython_setof.out
index 170dbc394de..39409400290 100644
--- a/src/pl/plpython/expected/plpython_setof.out
+++ b/src/pl/plpython/expected/plpython_setof.out
@@ -3,20 +3,20 @@
 --
 CREATE FUNCTION test_setof_error() RETURNS SETOF text AS $$
 return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_setof_error();
 ERROR:  returned object cannot be iterated
 DETAIL:  PL/Python set-returning functions must return an iterable object.
 CONTEXT:  PL/Python function "test_setof_error"
 CREATE FUNCTION test_setof_as_list(count integer, content text) RETURNS SETOF text AS $$
 return [ content ]*count
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_as_tuple(count integer, content text) RETURNS SETOF text AS $$
 t = ()
 for i in range(count):
 	t += ( content, )
 return t
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_as_iterator(count integer, content text) RETURNS SETOF text AS $$
 class producer:
 	def __init__ (self, icount, icontent):
@@ -24,13 +24,13 @@ class producer:
 		self.icount = icount
 	def __iter__ (self):
 		return self
-	def next (self):
+	def __next__ (self):
 		if self.icount == 0:
 			raise StopIteration
 		self.icount -= 1
 		return self.icontent
 return producer(count, content)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_spi_in_iterator() RETURNS SETOF text AS
 $$
     for s in ('Hello', 'Brave', 'New', 'World'):
@@ -38,7 +38,7 @@ $$
         yield s
         plpy.execute('select 2')
 $$
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 -- Test set returning functions
 SELECT test_setof_as_list(0, 'list');
  test_setof_as_list 
@@ -130,7 +130,7 @@ global x
 while x <= lim:
     yield x
     x = x + 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT ugly(1, 5);
  ugly 
 ------
@@ -155,7 +155,7 @@ CREATE OR REPLACE FUNCTION get_user_records()
 RETURNS SETOF users
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT get_user_records();
    get_user_records   
 ----------------------
@@ -179,7 +179,7 @@ CREATE OR REPLACE FUNCTION get_user_records2()
 RETURNS TABLE(fname text, lname text, username text, userid int)
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT get_user_records2();
   get_user_records2   
 ----------------------
diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out
index a09df68c7d1..391fdb0e645 100644
--- a/src/pl/plpython/expected/plpython_spi.out
+++ b/src/pl/plpython/expected/plpython_spi.out
@@ -6,17 +6,17 @@ CREATE FUNCTION nested_call_one(a text) RETURNS text
 'q = "SELECT nested_call_two(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 CREATE FUNCTION nested_call_two(a text) RETURNS text
 	AS
 'q = "SELECT nested_call_three(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 CREATE FUNCTION nested_call_three(a text) RETURNS text
 	AS
 'return a'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 -- some spi stuff
 CREATE FUNCTION spi_prepared_plan_test_one(a text) RETURNS text
 	AS
@@ -30,7 +30,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
 	AS
 'if "myplan" not in SD:
@@ -43,7 +43,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
 	AS
 'if "myplan" not in SD:
@@ -57,7 +57,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION join_sequences(s sequences) RETURNS text
 	AS
 'if not s["multipart"]:
@@ -69,7 +69,7 @@ for r in rv:
 	seq = seq + r["sequence"]
 return seq
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
 	AS
 'r = 0
@@ -77,7 +77,7 @@ if a > 1:
     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
 return a + r
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 --
 -- spi and nested calls
 --
@@ -155,7 +155,7 @@ if result.status() > 0:
    return result.nrows()
 else:
    return None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
 INFO:  True
 INFO:  ['foo', 'bar']
@@ -177,7 +177,7 @@ CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return result.nrows()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_nrows_test($$SELECT 1$$);
  result_nrows_test 
 -------------------
@@ -206,7 +206,7 @@ CREATE FUNCTION result_len_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return len(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_len_test($$SELECT 1$$);
  result_len_test 
 -----------------
@@ -254,7 +254,7 @@ except TypeError:
 else:
     assert False, "TypeError not raised"
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_subscript_test();
 INFO:  2
 INFO:  4
@@ -272,7 +272,7 @@ result = plpy.execute("select 1 where false")
 
 plpy.info(result[:])
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_empty_test();
 INFO:  []
  result_empty_test 
@@ -285,7 +285,7 @@ AS $$
 plan = plpy.prepare(cmd)
 result = plpy.execute(plan)
 return str(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
                       result_str_test                       
 ------------------------------------------------------------
@@ -306,12 +306,12 @@ for row in res:
     if row['lname'] == 'doe':
         does += 1
 return does
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
 res.close()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 assert len(res.fetch(3)) == 3
@@ -329,7 +329,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users order by fname")
 assert len(res.fetch(2)) == 2
@@ -342,7 +342,7 @@ except AttributeError:
 assert item['fname'] == 'rick'
 
 assert len(res.fetch(2)) == 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
@@ -352,7 +352,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION next_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
@@ -365,7 +365,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users where false")
 assert len(res.fetch(1)) == 0
@@ -378,7 +378,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
 plan = plpy.prepare(
     "select fname, lname from users where fname like $1 || '%' order by fname",
@@ -387,12 +387,12 @@ for row in plpy.cursor(plan, ["w"]):
     yield row['fname']
 for row in plan.cursor(["j"]):
     yield row['fname']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
                     ["text"])
 c = plpy.cursor(plan, ["a", "b"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TYPE test_composite_type AS (
   a1 int,
   a2 varchar
@@ -401,7 +401,7 @@ CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS
 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
 return res[0]["c1"]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT simple_cursor_test();
  simple_cursor_test 
 --------------------
diff --git a/src/pl/plpython/expected/plpython_subtransaction.out b/src/pl/plpython/expected/plpython_subtransaction.out
index 2a56541917d..43d9277a33b 100644
--- a/src/pl/plpython/expected/plpython_subtransaction.out
+++ b/src/pl/plpython/expected/plpython_subtransaction.out
@@ -14,7 +14,7 @@ with plpy.subtransaction():
         plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
     elif what_error == "Python":
         raise Exception("Python exception")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_ctx_test();
  subtransaction_ctx_test 
 -------------------------
@@ -71,7 +71,7 @@ with plpy.subtransaction():
             raise
         plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_nested_test();
 ERROR:  spiexceptions.SyntaxError: syntax error at or near "error"
 LINE 1: error
@@ -111,7 +111,7 @@ with plpy.subtransaction():
     plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
     plpy.execute("SELECT subtransaction_nested_test('t')")
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_deeply_nested_test();
 NOTICE:  Swallowed SyntaxError('syntax error at or near "error"')
  subtransaction_deeply_nested_test 
@@ -133,42 +133,42 @@ TRUNCATE subtransaction_tbl;
 CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
 AS $$
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__exit__(None, None, None)
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
 AS $$
 s = plpy.subtransaction()
 s.__enter__()
 s.__exit__(None, None, None)
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
 AS $$
 s = plpy.subtransaction()
 s.__enter__()
 s.__enter__()
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- No warnings here, as the subtransaction gets indeed closed
 CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
 AS $$
 with plpy.subtransaction() as s:
     s.__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
 AS $$
 try:
@@ -176,7 +176,7 @@ try:
         s.__exit__(None, None, None)
 except ValueError as e:
     raise ValueError(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_exit_without_enter();
 ERROR:  ValueError: this subtransaction has not been entered
 CONTEXT:  Traceback (most recent call last):
@@ -255,7 +255,7 @@ try:
     plpy.execute(p, ["wrong"])
 except plpy.SPIError:
     plpy.warning("Caught a SPI error")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_mix_explicit_and_implicit();
 WARNING:  Caught a SPI error from an explicit subtransaction
 WARNING:  Caught a SPI error
@@ -278,7 +278,7 @@ AS $$
 s = plpy.subtransaction()
 s.enter()
 s.exit(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_alternative_names();
  subtransaction_alternative_names 
 ----------------------------------
@@ -294,7 +294,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT try_catch_inside_subtransaction();
 NOTICE:  caught
  try_catch_inside_subtransaction 
@@ -318,7 +318,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT pk_violation_inside_subtransaction();
 NOTICE:  caught
  pk_violation_inside_subtransaction 
@@ -340,7 +340,7 @@ with plpy.subtransaction():
     cur.fetch(10)
 fetched = cur.fetch(10);
 return int(fetched[5]["i"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
 try:
     with plpy.subtransaction():
@@ -351,7 +351,7 @@ except plpy.SPIError:
     fetched = cur.fetch(10)
     return int(fetched[5]["i"])
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
 try:
     with plpy.subtransaction():
@@ -364,7 +364,7 @@ except plpy.SPIError:
     fetched = cur.fetch(5)
     return fetched[2]["i"]
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
 try:
     with plpy.subtransaction():
@@ -374,7 +374,7 @@ except plpy.SPIError:
     cur.close()
     return True
 return False # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT cursor_in_subxact();
  cursor_in_subxact 
 -------------------
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index 39b994f4468..13c14119c08 100644
--- a/src/pl/plpython/expected/plpython_test.out
+++ b/src/pl/plpython/expected/plpython_test.out
@@ -1,7 +1,7 @@
 -- first some tests of basic functionality
-CREATE EXTENSION plpython2u;
+CREATE EXTENSION plpython3u;
 -- really stupid function just to get the module loaded
-CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
+CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 select stupid();
  stupid 
 --------
@@ -9,7 +9,7 @@ select stupid();
 (1 row)
 
 -- check 2/3 versioning
-CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
+CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 select stupidn();
  stupidn 
 ---------
@@ -26,7 +26,7 @@ for key in keys:
     out.append("%s: %s" % (key, u[key]))
 words = a1 + " " + a2 + " => {" + ", ".join(out) + "}"
 return words'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 select "Argument test #1"(users, fname, lname) from users where lname = 'doe' order by 1;
                            Argument test #1                            
 -----------------------------------------------------------------------
@@ -41,7 +41,7 @@ $$
 contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
 contents.sort()
 return contents
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select module_contents();
  module_contents 
 -----------------
@@ -78,7 +78,7 @@ plpy.info('info', 37, [1, 2, 3])
 plpy.notice('notice')
 plpy.warning('warning')
 plpy.error('error')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT elog_test_basic();
 INFO:  info
 INFO:  37
diff --git a/src/pl/plpython/expected/plpython_transaction.out b/src/pl/plpython/expected/plpython_transaction.out
index 14152993c75..393ea21eaad 100644
--- a/src/pl/plpython/expected/plpython_transaction.out
+++ b/src/pl/plpython/expected/plpython_transaction.out
@@ -1,6 +1,6 @@
 CREATE TABLE test1 (a int, b text);
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -22,7 +22,7 @@ SELECT * FROM test1;
 
 TRUNCATE test1;
 DO
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -44,7 +44,7 @@ SELECT * FROM test1;
 TRUNCATE test1;
 -- not allowed in a function
 CREATE FUNCTION transaction_test2() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -64,7 +64,7 @@ SELECT * FROM test1;
 
 -- also not allowed if procedure is called from a function
 CREATE FUNCTION transaction_test3() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("CALL transaction_test1()")
 return 1
@@ -82,19 +82,19 @@ SELECT * FROM test1;
 
 -- DO block inside function
 CREATE FUNCTION transaction_test4() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 return 1
 $$;
 SELECT transaction_test4();
 ERROR:  spiexceptions.InvalidTransactionTermination: invalid transaction termination
 CONTEXT:  Traceback (most recent call last):
   PL/Python function "transaction_test4", line 2, in <module>
-    plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+    plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 PL/Python function "transaction_test4"
 -- commit inside subtransaction (prohibited)
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 s = plpy.subtransaction()
 s.enter()
 plpy.commit()
@@ -106,7 +106,7 @@ CONTEXT:  PL/Python anonymous code block
 CREATE TABLE test2 (x int);
 INSERT INTO test2 VALUES (0), (1), (2), (3), (4);
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.commit()
@@ -129,7 +129,7 @@ SELECT * FROM pg_cursors;
 
 -- error in cursor loop with commit
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x'])
     plpy.commit()
@@ -153,7 +153,7 @@ SELECT * FROM pg_cursors;
 
 -- rollback inside cursor loop
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.rollback()
@@ -170,7 +170,7 @@ SELECT * FROM pg_cursors;
 
 -- first commit then rollback inside cursor loop
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     if row['x'] % 2 == 0:
diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out
index 742988a5b59..dd1ca32fa49 100644
--- a/src/pl/plpython/expected/plpython_trigger.out
+++ b/src/pl/plpython/expected/plpython_trigger.out
@@ -15,20 +15,20 @@ if TD["new"]["fname"] == "william":
 	TD["new"]["fname"] = TD["args"][0]
 	rv = "MODIFY"
 return rv'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION users_update() returns trigger
 	AS
 'if TD["event"] == "UPDATE":
 	if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
 		return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION users_delete() RETURNS trigger
 	AS
 'if TD["old"]["fname"] == TD["args"][0]:
 	return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
 	EXECUTE PROCEDURE users_insert ('willem');
 CREATE TRIGGER users_update_trig BEFORE UPDATE ON users FOR EACH ROW
@@ -71,7 +71,7 @@ CREATE TABLE trigger_test_generated (
 	i int,
         j int GENERATED ALWAYS AS (i * 2) STORED
 );
-CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
 
 if 'relid' in TD:
 	TD['relid'] = "bogus:12345"
@@ -328,7 +328,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid1() RETURNS trigger
 AS $$
     return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger1
 BEFORE INSERT ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid1();
@@ -341,7 +341,7 @@ DROP TRIGGER stupid_trigger1 ON trigger_test;
 CREATE FUNCTION stupid2() RETURNS trigger
 AS $$
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger2
 BEFORE DELETE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid2();
@@ -353,7 +353,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid3() RETURNS trigger
 AS $$
     return "foo"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid3();
@@ -365,8 +365,8 @@ DROP TRIGGER stupid_trigger3 ON trigger_test;
 -- Unicode variant
 CREATE FUNCTION stupid3u() RETURNS trigger
 AS $$
-    return u"foo"
-$$ LANGUAGE plpythonu;
+    return "foo"
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid3u();
@@ -380,7 +380,7 @@ CREATE FUNCTION stupid4() RETURNS trigger
 AS $$
     del TD["new"]
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger4
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid4();
@@ -394,7 +394,7 @@ CREATE FUNCTION stupid5() RETURNS trigger
 AS $$
     TD["new"] = ['foo', 'bar']
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger5
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid5();
@@ -408,7 +408,7 @@ CREATE FUNCTION stupid6() RETURNS trigger
 AS $$
     TD["new"] = {1: 'foo', 2: 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger6
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid6();
@@ -422,7 +422,7 @@ CREATE FUNCTION stupid7() RETURNS trigger
 AS $$
     TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid7();
@@ -434,9 +434,9 @@ DROP TRIGGER stupid_trigger7 ON trigger_test;
 -- Unicode variant
 CREATE FUNCTION stupid7u() RETURNS trigger
 AS $$
-    TD["new"] = {u'v': 'foo', u'a': 'bar'}
+    TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid7u();
@@ -461,7 +461,7 @@ CREATE FUNCTION test_null() RETURNS trigger
 AS $$
     TD["new"]['v'] = None
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER test_null_trigger
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE test_null();
@@ -481,7 +481,7 @@ SET DateStyle = 'ISO';
 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
 CREATE TRIGGER set_modif_time BEFORE UPDATE ON pb
   FOR EACH ROW EXECUTE PROCEDURE set_modif_time();
@@ -507,7 +507,7 @@ CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
     TD['new']['f1'] = (3, False)
     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
 INSERT INTO composite_trigger_test VALUES (NULL, NULL);
@@ -521,7 +521,7 @@ SELECT * FROM composite_trigger_test;
 CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
 INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL);
@@ -540,7 +540,7 @@ CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer);
 CREATE TABLE composite_trigger_nested_test(c comp3);
 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
 INSERT INTO composite_trigger_nested_test VALUES (NULL);
@@ -555,7 +555,7 @@ SELECT * FROM composite_trigger_nested_test;
 (3 rows)
 
 -- check that using a function as a trigger over two tables works correctly
-CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
     TD["new"]["data"] = '1234'
     return 'MODIFY'
 $$;
@@ -581,7 +581,7 @@ SELECT * FROM b;
 -- check that SQL run in trigger code can see transition tables
 CREATE TABLE transition_table_test (id int, name text);
 INSERT INTO transition_table_test VALUES (1, 'a');
-CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
 $$
     rv = plpy.execute("SELECT * FROM old_table")
     assert(rv.nrows() == 1)
@@ -601,7 +601,7 @@ DROP TABLE transition_table_test;
 DROP FUNCTION transition_table_test_f();
 -- dealing with generated columns
 CREATE FUNCTION generated_test_func1() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 TD['new']['j'] = 5  # not allowed
 return 'MODIFY'
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index 0a2659fe292..a470911c2ec 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -7,23 +7,23 @@
 CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bool(true);
-INFO:  (True, <type 'bool'>)
+INFO:  (True, <class 'bool'>)
  test_type_conversion_bool 
 ---------------------------
  t
 (1 row)
 
 SELECT * FROM test_type_conversion_bool(false);
-INFO:  (False, <type 'bool'>)
+INFO:  (False, <class 'bool'>)
  test_type_conversion_bool 
 ---------------------------
  f
 (1 row)
 
 SELECT * FROM test_type_conversion_bool(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_bool 
 ---------------------------
  
@@ -48,7 +48,7 @@ elif n == 5:
    ret = [0]
 plpy.info(ret, not not ret)
 return ret
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bool_other(0);
 INFO:  (0, False)
  test_type_conversion_bool_other 
@@ -94,16 +94,16 @@ INFO:  ([0], True)
 CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_char('a');
-INFO:  ('a', <type 'str'>)
+INFO:  ('a', <class 'str'>)
  test_type_conversion_char 
 ---------------------------
  a
 (1 row)
 
 SELECT * FROM test_type_conversion_char(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_char 
 ---------------------------
  
@@ -112,23 +112,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int2(100::int2);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int2 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int2(-100::int2);
-INFO:  (-100, <type 'int'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int2 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int2(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int2 
 ---------------------------
                           
@@ -137,23 +137,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int4(100);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int4 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int4(-100);
-INFO:  (-100, <type 'int'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int4 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int4(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int4 
 ---------------------------
                           
@@ -162,30 +162,30 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int8(100);
-INFO:  (100L, <type 'long'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(-100);
-INFO:  (-100L, <type 'long'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(5000000000);
-INFO:  (5000000000L, <type 'long'>)
+INFO:  (5000000000, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                 5000000000
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int8 
 ---------------------------
                           
@@ -196,7 +196,7 @@ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
 # between decimal and cdecimal
 plpy.info(str(x), x.__class__.__name__)
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_numeric(100);
 INFO:  ('100', 'Decimal')
  test_type_conversion_numeric 
@@ -256,30 +256,30 @@ INFO:  ('None', 'NoneType')
 CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_float4(100);
-INFO:  (100.0, <type 'float'>)
+INFO:  (100.0, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                          100
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(-100);
-INFO:  (-100.0, <type 'float'>)
+INFO:  (-100.0, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                         -100
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(5000.5);
-INFO:  (5000.5, <type 'float'>)
+INFO:  (5000.5, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                       5000.5
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_float4 
 -----------------------------
                             
@@ -288,37 +288,37 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_float8(100);
-INFO:  (100.0, <type 'float'>)
+INFO:  (100.0, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                          100
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(-100);
-INFO:  (-100.0, <type 'float'>)
+INFO:  (-100.0, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                         -100
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(5000000000.5);
-INFO:  (5000000000.5, <type 'float'>)
+INFO:  (5000000000.5, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                 5000000000.5
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_float8 
 -----------------------------
                             
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(100100100.654321);
-INFO:  (100100100.654321, <type 'float'>)
+INFO:  (100100100.654321, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
             100100100.654321
@@ -327,23 +327,23 @@ INFO:  (100100100.654321, <type 'float'>)
 CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_oid(100);
-INFO:  (100L, <type 'long'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_oid 
 --------------------------
                       100
 (1 row)
 
 SELECT * FROM test_type_conversion_oid(2147483649);
-INFO:  (2147483649L, <type 'long'>)
+INFO:  (2147483649, <class 'int'>)
  test_type_conversion_oid 
 --------------------------
                2147483649
 (1 row)
 
 SELECT * FROM test_type_conversion_oid(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_oid 
 --------------------------
                          
@@ -352,16 +352,16 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_text('hello world');
-INFO:  ('hello world', <type 'str'>)
+INFO:  ('hello world', <class 'str'>)
  test_type_conversion_text 
 ---------------------------
  hello world
 (1 row)
 
 SELECT * FROM test_type_conversion_text(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_text 
 ---------------------------
  
@@ -370,23 +370,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bytea('hello world');
-INFO:  ('hello world', <type 'str'>)
+INFO:  (b'hello world', <class 'bytes'>)
  test_type_conversion_bytea 
 ----------------------------
  \x68656c6c6f20776f726c64
 (1 row)
 
 SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
-INFO:  ('null\x00byte', <type 'str'>)
+INFO:  (b'null\x00byte', <class 'bytes'>)
  test_type_conversion_bytea 
 ----------------------------
  \x6e756c6c0062797465
 (1 row)
 
 SELECT * FROM test_type_conversion_bytea(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_bytea 
 ----------------------------
  
@@ -395,14 +395,14 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
 import marshal
 return marshal.dumps('hello world')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
 import marshal
 try:
     return marshal.loads(x)
 except ValueError as e:
     return 'FAILED: ' + str(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
  test_type_unmarshal 
 ---------------------
@@ -415,7 +415,7 @@ SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
 CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
 CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_booltrue(true, true);
  test_type_conversion_booltrue 
 -------------------------------
@@ -432,21 +432,21 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
 CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_uint2 
 ----------------------------
                          50
 (1 row)
 
 SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
 ERROR:  value for domain uint2 violates check constraint "uint2_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_uint2"
 SELECT * FROM test_type_conversion_uint2(null, 1);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_uint2 
 ----------------------------
                           1
@@ -455,7 +455,7 @@ INFO:  (None, <type 'NoneType'>)
 CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
 CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_nnint(10, 20);
  test_type_conversion_nnint 
 ----------------------------
@@ -472,9 +472,9 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
 CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
-INFO:  ('hello wold', <type 'str'>)
+INFO:  (b'hello wold', <class 'bytes'>)
  test_type_conversion_bytea10 
 ------------------------------
  \x68656c6c6f20776f6c64
@@ -483,14 +483,14 @@ INFO:  ('hello wold', <type 'str'>)
 SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
-INFO:  ('hello word', <type 'str'>)
+INFO:  (b'hello word', <class 'bytes'>)
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_bytea10"
 SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 SELECT * FROM test_type_conversion_bytea10('hello word', null);
-INFO:  ('hello word', <type 'str'>)
+INFO:  (b'hello word', <class 'bytes'>)
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_bytea10"
@@ -500,58 +500,58 @@ PL/Python function "test_type_conversion_bytea10"
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
-INFO:  ([0, 100], <type 'list'>)
+INFO:  ([0, 100], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {0,100}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
-INFO:  ([0, -100, 55], <type 'list'>)
+INFO:  ([0, -100, 55], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {0,-100,55}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
-INFO:  ([None, 1], <type 'list'>)
+INFO:  ([None, 1], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {NULL,1}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
-INFO:  ([], <type 'list'>)
+INFO:  ([], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(NULL);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_array_int4 
 ---------------------------------
  
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-INFO:  ([[1, 2, 3], [4, 5, 6]], <type 'list'>)
+INFO:  ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {{1,2,3},{4,5,6}}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <type 'list'>)
+INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
           test_type_conversion_array_int4          
 ---------------------------------------------------
  {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
-INFO:  ([1, 2, 3], <type 'list'>)
+INFO:  ([1, 2, 3], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {1,2,3}
@@ -560,9 +560,9 @@ INFO:  ([1, 2, 3], <type 'list'>)
 CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
-INFO:  ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <type 'list'>)
+INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
           test_type_conversion_array_int8          
 ---------------------------------------------------
  {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
@@ -571,10 +571,10 @@ INFO:  ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <
 CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
             [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
-INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <type 'list'>)
+INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
                                                  test_type_conversion_array_date                                                 
 ---------------------------------------------------------------------------------------------------------------------------------
  {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
@@ -583,12 +583,12 @@ INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']
 CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
             [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
             [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
             ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
-INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <type 'list'>)
+INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
                                                                                                                                                       test_type_conversion_array_timestamp                                                                                                                                                      
 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
@@ -598,9 +598,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemint4(8,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
                                                                                                                                                                                                                                                                              pyreturnmultidemint4                                                                                                                                                                                                                                                                              
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
@@ -610,9 +610,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemint8(5,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
                                                                                                                                                                                    pyreturnmultidemint8                                                                                                                                                                                    
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
@@ -622,9 +622,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemfloat4(6,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
                                                                                                                                                                                                                 pyreturnmultidemfloat4                                                                                                                                                                                                                 
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
@@ -634,9 +634,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemfloat8(7,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
                                                                                                                                                                                                                                               pyreturnmultidemfloat8                                                                                                                                                                                                                                               
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
@@ -645,16 +645,16 @@ INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6],
 CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
-INFO:  (['foo', 'bar'], <type 'list'>)
+INFO:  (['foo', 'bar'], <class 'list'>)
  test_type_conversion_array_text 
 ---------------------------------
  {foo,bar}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
-INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
+INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
  test_type_conversion_array_text 
 ---------------------------------
  {{foo,bar},{foo2,bar2}}
@@ -663,9 +663,9 @@ INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
 CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
-INFO:  (['\xde\xad\xbe\xef', None], <type 'list'>)
+INFO:  ([b'\xde\xad\xbe\xef', None], <class 'list'>)
  test_type_conversion_array_bytea 
 ----------------------------------
  {"\\xdeadbeef",NULL}
@@ -673,7 +673,7 @@ INFO:  (['\xde\xad\xbe\xef', None], <type 'list'>)
 
 CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_mixed1();
  test_type_conversion_array_mixed1 
 -----------------------------------
@@ -682,14 +682,14 @@ SELECT * FROM test_type_conversion_array_mixed1();
 
 CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_mixed2();
 ERROR:  invalid input syntax for type integer: "abc"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_array_mixed2"
 CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
 return [[1,2,3],[4,5]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_mdarray_malformed();
 ERROR:  wrong length of inner sequence: has length 2, but 3 was expected
 DETAIL:  To construct a multidimensional array, the inner sequences must all have the same length.
@@ -697,14 +697,14 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_mdarray_malformed"
 CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
 return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_mdarray_toodeep();
 ERROR:  number of array dimensions exceeds the maximum allowed (6)
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_mdarray_toodeep"
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_record();
  test_type_conversion_array_record 
 -----------------------------------
@@ -713,7 +713,7 @@ SELECT * FROM test_type_conversion_array_record();
 
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_string();
  test_type_conversion_array_string 
 -----------------------------------
@@ -722,7 +722,7 @@ SELECT * FROM test_type_conversion_array_string();
 
 CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
 return ('abc', 'def')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_tuple();
  test_type_conversion_array_tuple 
 ----------------------------------
@@ -731,7 +731,7 @@ SELECT * FROM test_type_conversion_array_tuple();
 
 CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
 return 5
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_error();
 ERROR:  return value of function with array return type is not a Python sequence
 CONTEXT:  while creating return value
@@ -743,16 +743,16 @@ CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AN
 CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
-INFO:  ([0, 100], <type 'list'>)
+INFO:  ([0, 100], <class 'list'>)
  test_type_conversion_array_domain 
 -----------------------------------
  {0,100}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_array_domain 
 -----------------------------------
  
@@ -760,7 +760,7 @@ INFO:  (None, <type 'NoneType'>)
 
 CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
 return [2,1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain_check_violation();
 ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
 CONTEXT:  while creating return value
@@ -771,9 +771,9 @@ PL/Python function "test_type_conversion_array_domain_check_violation"
 CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_read_uint2_array(array[1::uint2]);
-INFO:  ([1], <type 'list'>)
+INFO:  ([1], <class 'list'>)
  test_read_uint2_array 
 -----------------------
                      1
@@ -781,7 +781,7 @@ INFO:  ([1], <type 'list'>)
 
 CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_build_uint2_array(1::int2);
  test_build_uint2_array 
 ------------------------
@@ -800,7 +800,7 @@ PL/Python function "test_build_uint2_array"
 CREATE FUNCTION test_type_conversion_domain_array(x integer[])
   RETURNS ordered_pair_domain[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_domain_array(array[2,4]);
 ERROR:  return value of function with array return type is not a Python sequence
 CONTEXT:  while creating return value
@@ -813,9 +813,9 @@ CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
   RETURNS integer AS $$
 plpy.info(x, type(x))
 return x[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_domain_array2(array[2,4]);
-INFO:  ([2, 4], <type 'list'>)
+INFO:  ([2, 4], <class 'list'>)
  test_type_conversion_domain_array2 
 ------------------------------------
                                   4
@@ -827,9 +827,9 @@ CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
   RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
-INFO:  ([[2, 4]], <type 'list'>)
+INFO:  ([[2, 4]], <class 'list'>)
  test_type_conversion_array_domain_array 
 -----------------------------------------
  {2,4}
@@ -846,7 +846,7 @@ CREATE TABLE employee (
 INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
 CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
 return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT name, test_composite_table_input(employee.*) FROM employee;
  name | test_composite_table_input 
 ------+----------------------------
@@ -876,7 +876,7 @@ CREATE TYPE named_pair AS (
 );
 CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
 return sum(p.values())
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_composite_type_input(row(1, 2));
  test_composite_type_input 
 ---------------------------
@@ -896,7 +896,7 @@ SELECT test_composite_type_input(row(1, 2));
 CREATE TYPE nnint_container AS (f1 int, f2 nnint);
 CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
 return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT nnint_test(null, 3);
  nnint_test 
 ------------
@@ -913,7 +913,7 @@ PL/Python function "nnint_test"
 CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
 CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
 return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT read_ordered_named_pair(row(1, 2));
  read_ordered_named_pair 
 -------------------------
@@ -924,7 +924,7 @@ SELECT read_ordered_named_pair(row(2, 1));  -- fail
 ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
 CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
 return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT build_ordered_named_pair(1,2);
  build_ordered_named_pair 
 --------------------------
@@ -937,7 +937,7 @@ CONTEXT:  while creating return value
 PL/Python function "build_ordered_named_pair"
 CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
 return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT build_ordered_named_pairs(1,2);
  build_ordered_named_pairs 
 ---------------------------
@@ -952,7 +952,7 @@ PL/Python function "build_ordered_named_pairs"
 -- Prepared statements
 --
 CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
 rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
@@ -965,7 +965,7 @@ SELECT test_prep_bool_input(); -- 1
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
 rv = plpy.execute(plan, [0], 5)
@@ -980,7 +980,7 @@ INFO:  {'val': False}
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
 rv = plpy.execute(plan, [bb], 5)
@@ -993,7 +993,7 @@ SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
 rv = plpy.execute(plan, [], 5)
@@ -1001,7 +1001,7 @@ plpy.info(rv[0])
 return rv[0]['val']
 $$;
 SELECT test_prep_bytea_output();
-INFO:  {'val': '\xaa\x00\xbb'}
+INFO:  {'val': b'\xaa\x00\xbb'}
  test_prep_bytea_output 
 ------------------------
  \xaa00bb
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
deleted file mode 100644
index a6ec10d5e18..00000000000
--- a/src/pl/plpython/expected/plpython_types_3.out
+++ /dev/null
@@ -1,1009 +0,0 @@
---
--- Test data type behavior
---
---
--- Base/common types
---
-CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bool(true);
-INFO:  (True, <class 'bool'>)
- test_type_conversion_bool 
----------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool(false);
-INFO:  (False, <class 'bool'>)
- test_type_conversion_bool 
----------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_bool 
----------------------------
- 
-(1 row)
-
--- test various other ways to express Booleans in Python
-CREATE FUNCTION test_type_conversion_bool_other(n int) RETURNS bool AS $$
-# numbers
-if n == 0:
-   ret = 0
-elif n == 1:
-   ret = 5
-# strings
-elif n == 2:
-   ret = ''
-elif n == 3:
-   ret = 'fa' # true in Python, false in PostgreSQL
-# containers
-elif n == 4:
-   ret = []
-elif n == 5:
-   ret = [0]
-plpy.info(ret, not not ret)
-return ret
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bool_other(0);
-INFO:  (0, False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(1);
-INFO:  (5, True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(2);
-INFO:  ('', False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(3);
-INFO:  ('fa', True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(4);
-INFO:  ([], False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(5);
-INFO:  ([0], True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_char('a');
-INFO:  ('a', <class 'str'>)
- test_type_conversion_char 
----------------------------
- a
-(1 row)
-
-SELECT * FROM test_type_conversion_char(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_char 
----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int2(100::int2);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int2 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int2(-100::int2);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int2 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int2(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int2 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int4(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int4 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int4(-100);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int4 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int4(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int4 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int8(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(-100);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(5000000000);
-INFO:  (5000000000, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                5000000000
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int8 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
-# print just the class name, not the type, to avoid differences
-# between decimal and cdecimal
-plpy.info(str(x), x.__class__.__name__)
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_numeric(100);
-INFO:  ('100', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                          100
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(-100);
-INFO:  ('-100', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                         -100
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(100.0);
-INFO:  ('100.0', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                        100.0
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(100.00);
-INFO:  ('100.00', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                       100.00
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(5000000000.5);
-INFO:  ('5000000000.5', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                 5000000000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(1234567890.0987654321);
-INFO:  ('1234567890.0987654321', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-        1234567890.0987654321
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(-1234567890.0987654321);
-INFO:  ('-1234567890.0987654321', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-       -1234567890.0987654321
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(null);
-INFO:  ('None', 'NoneType')
- test_type_conversion_numeric 
-------------------------------
-                             
-(1 row)
-
-CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_float4(100);
-INFO:  (100.0, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                         100
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(-100);
-INFO:  (-100.0, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                        -100
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(5000.5);
-INFO:  (5000.5, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                      5000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_float4 
------------------------------
-                            
-(1 row)
-
-CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_float8(100);
-INFO:  (100.0, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                         100
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(-100);
-INFO:  (-100.0, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                        -100
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(5000000000.5);
-INFO:  (5000000000.5, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                5000000000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_float8 
------------------------------
-                            
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(100100100.654321);
-INFO:  (100100100.654321, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-            100100100.654321
-(1 row)
-
-CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_oid(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_oid 
---------------------------
-                      100
-(1 row)
-
-SELECT * FROM test_type_conversion_oid(2147483649);
-INFO:  (2147483649, <class 'int'>)
- test_type_conversion_oid 
---------------------------
-               2147483649
-(1 row)
-
-SELECT * FROM test_type_conversion_oid(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_oid 
---------------------------
-                         
-(1 row)
-
-CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_text('hello world');
-INFO:  ('hello world', <class 'str'>)
- test_type_conversion_text 
----------------------------
- hello world
-(1 row)
-
-SELECT * FROM test_type_conversion_text(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_text 
----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bytea('hello world');
-INFO:  (b'hello world', <class 'bytes'>)
- test_type_conversion_bytea 
-----------------------------
- \x68656c6c6f20776f726c64
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
-INFO:  (b'null\x00byte', <class 'bytes'>)
- test_type_conversion_bytea 
-----------------------------
- \x6e756c6c0062797465
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_bytea 
-----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
-import marshal
-return marshal.dumps('hello world')
-$$ LANGUAGE plpython3u;
-CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
-import marshal
-try:
-    return marshal.loads(x)
-except ValueError as e:
-    return 'FAILED: ' + str(e)
-$$ LANGUAGE plpython3u;
-SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
- test_type_unmarshal 
----------------------
- hello world
-(1 row)
-
---
--- Domains
---
-CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
-CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_booltrue(true, true);
- test_type_conversion_booltrue 
--------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_booltrue(false, true);
-ERROR:  value for domain booltrue violates check constraint "booltrue_check"
-SELECT * FROM test_type_conversion_booltrue(true, false);
-ERROR:  value for domain booltrue violates check constraint "booltrue_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_booltrue"
-CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
-CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
-plpy.info(x, type(x))
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
-INFO:  (100, <class 'int'>)
- test_type_conversion_uint2 
-----------------------------
-                         50
-(1 row)
-
-SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
-INFO:  (100, <class 'int'>)
-ERROR:  value for domain uint2 violates check constraint "uint2_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_uint2"
-SELECT * FROM test_type_conversion_uint2(null, 1);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_uint2 
-----------------------------
-                          1
-(1 row)
-
-CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
-CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_nnint(10, 20);
- test_type_conversion_nnint 
-----------------------------
-                         20
-(1 row)
-
-SELECT * FROM test_type_conversion_nnint(null, 20);
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-SELECT * FROM test_type_conversion_nnint(10, null);
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_nnint"
-CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL);
-CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
-plpy.info(x, type(x))
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
-INFO:  (b'hello wold', <class 'bytes'>)
- test_type_conversion_bytea10 
-------------------------------
- \x68656c6c6f20776f6c64
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
-INFO:  (b'hello word', <class 'bytes'>)
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_bytea10"
-SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-SELECT * FROM test_type_conversion_bytea10('hello word', null);
-INFO:  (b'hello word', <class 'bytes'>)
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_bytea10"
---
--- Arrays
---
-CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
-INFO:  ([0, 100], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {0,100}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
-INFO:  ([0, -100, 55], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {0,-100,55}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
-INFO:  ([None, 1], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {NULL,1}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
-INFO:  ([], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(NULL);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_array_int4 
----------------------------------
- 
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-INFO:  ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {{1,2,3},{4,5,6}}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
-          test_type_conversion_array_int4          
----------------------------------------------------
- {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
-INFO:  ([1, 2, 3], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {1,2,3}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
-          test_type_conversion_array_int8          
----------------------------------------------------
- {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
-            [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
-INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
-                                                 test_type_conversion_array_date                                                 
----------------------------------------------------------------------------------------------------------------------------------
- {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
-            [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
-            [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
-            ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
-INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
-                                                                                                                                                      test_type_conversion_array_timestamp                                                                                                                                                      
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 ) RETURNS int4[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemint4(8,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
-                                                                                                                                                                                                                                                                             pyreturnmultidemint4                                                                                                                                                                                                                                                                              
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 ) RETURNS int8[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemint8(5,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
-                                                                                                                                                                                   pyreturnmultidemint8                                                                                                                                                                                    
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4 ) RETURNS float4[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemfloat4(6,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
-                                                                                                                                                                                                                pyreturnmultidemfloat4                                                                                                                                                                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4 ) RETURNS float8[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemfloat8(7,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
-                                                                                                                                                                                                                                              pyreturnmultidemfloat8                                                                                                                                                                                                                                               
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
-INFO:  (['foo', 'bar'], <class 'list'>)
- test_type_conversion_array_text 
----------------------------------
- {foo,bar}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
-INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
- test_type_conversion_array_text 
----------------------------------
- {{foo,bar},{foo2,bar2}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
-INFO:  ([b'\xde\xad\xbe\xef', None], <class 'list'>)
- test_type_conversion_array_bytea 
-----------------------------------
- {"\\xdeadbeef",NULL}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
-return [123, 'abc']
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_mixed1();
- test_type_conversion_array_mixed1 
------------------------------------
- {123,abc}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
-return [123, 'abc']
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_mixed2();
-ERROR:  invalid input syntax for type integer: "abc"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_mixed2"
-CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
-return [[1,2,3],[4,5]]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_mdarray_malformed();
-ERROR:  wrong length of inner sequence: has length 2, but 3 was expected
-DETAIL:  To construct a multidimensional array, the inner sequences must all have the same length.
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_mdarray_malformed"
-CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
-return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_mdarray_toodeep();
-ERROR:  number of array dimensions exceeds the maximum allowed (6)
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_mdarray_toodeep"
-CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
-return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_record();
- test_type_conversion_array_record 
------------------------------------
- {"(one,42)","(two,11)"}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
-return 'abc'
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_string();
- test_type_conversion_array_string 
------------------------------------
- {a,b,c}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
-return ('abc', 'def')
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_tuple();
- test_type_conversion_array_tuple 
-----------------------------------
- {abc,def}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
-return 5
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_error();
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_error"
---
--- Domains over arrays
---
-CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AND VALUE[1] < VALUE[2]);
-CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
-INFO:  ([0, 100], <class 'list'>)
- test_type_conversion_array_domain 
------------------------------------
- {0,100}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_array_domain 
------------------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
-return [2,1]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_domain_check_violation();
-ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_domain_check_violation"
---
--- Arrays of domains
---
-CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
-plpy.info(x, type(x))
-return x[0]
-$$ LANGUAGE plpythonu;
-select test_read_uint2_array(array[1::uint2]);
-INFO:  ([1], <class 'list'>)
- test_read_uint2_array 
------------------------
-                     1
-(1 row)
-
-CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
-return [x, x]
-$$ LANGUAGE plpythonu;
-select test_build_uint2_array(1::int2);
- test_build_uint2_array 
-------------------------
- {1,1}
-(1 row)
-
-select test_build_uint2_array(-1::int2);  -- fail
-ERROR:  value for domain uint2 violates check constraint "uint2_check"
-CONTEXT:  while creating return value
-PL/Python function "test_build_uint2_array"
---
--- ideally this would work, but for now it doesn't, because the return value
--- is [[2,4], [2,4]] which our conversion code thinks should become a 2-D
--- integer array, not an array of arrays.
---
-CREATE FUNCTION test_type_conversion_domain_array(x integer[])
-  RETURNS ordered_pair_domain[] AS $$
-return [x, x]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_domain_array(array[2,4]);
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_domain_array"
-select test_type_conversion_domain_array(array[4,2]);  -- fail
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_domain_array"
-CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
-  RETURNS integer AS $$
-plpy.info(x, type(x))
-return x[1]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_domain_array2(array[2,4]);
-INFO:  ([2, 4], <class 'list'>)
- test_type_conversion_domain_array2 
-------------------------------------
-                                  4
-(1 row)
-
-select test_type_conversion_domain_array2(array[4,2]);  -- fail
-ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
-CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
-  RETURNS ordered_pair_domain AS $$
-plpy.info(x, type(x))
-return x[0]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
-INFO:  ([[2, 4]], <class 'list'>)
- test_type_conversion_array_domain_array 
------------------------------------------
- {2,4}
-(1 row)
-
----
---- Composite types
----
-CREATE TABLE employee (
-    name text,
-    basesalary integer,
-    bonus integer
-);
-INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
-CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
-return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpython3u;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
- name | test_composite_table_input 
-------+----------------------------
- John |                        110
- Mary |                        210
-(2 rows)
-
-ALTER TABLE employee DROP bonus;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
-ERROR:  KeyError: 'bonus'
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "test_composite_table_input", line 2, in <module>
-    return e['basesalary'] + e['bonus']
-PL/Python function "test_composite_table_input"
-ALTER TABLE employee ADD bonus integer;
-UPDATE employee SET bonus = 10;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
- name | test_composite_table_input 
-------+----------------------------
- John |                        110
- Mary |                        210
-(2 rows)
-
-CREATE TYPE named_pair AS (
-    i integer,
-    j integer
-);
-CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
-return sum(p.values())
-$$ LANGUAGE plpython3u;
-SELECT test_composite_type_input(row(1, 2));
- test_composite_type_input 
----------------------------
-                         3
-(1 row)
-
-ALTER TYPE named_pair RENAME TO named_pair_2;
-SELECT test_composite_type_input(row(1, 2));
- test_composite_type_input 
----------------------------
-                         3
-(1 row)
-
---
--- Domains within composite
---
-CREATE TYPE nnint_container AS (f1 int, f2 nnint);
-CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
-return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
-SELECT nnint_test(null, 3);
- nnint_test 
-------------
- (,3)
-(1 row)
-
-SELECT nnint_test(3, null);  -- fail
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-CONTEXT:  while creating return value
-PL/Python function "nnint_test"
---
--- Domains of composite
---
-CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
-CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
-return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
-SELECT read_ordered_named_pair(row(1, 2));
- read_ordered_named_pair 
--------------------------
-                       3
-(1 row)
-
-SELECT read_ordered_named_pair(row(2, 1));  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
-return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
-SELECT build_ordered_named_pair(1,2);
- build_ordered_named_pair 
---------------------------
- (1,2)
-(1 row)
-
-SELECT build_ordered_named_pair(2,1);  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CONTEXT:  while creating return value
-PL/Python function "build_ordered_named_pair"
-CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
-return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
-SELECT build_ordered_named_pairs(1,2);
- build_ordered_named_pairs 
----------------------------
- {"(1,2)","(1,3)"}
-(1 row)
-
-SELECT build_ordered_named_pairs(2,1);  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CONTEXT:  while creating return value
-PL/Python function "build_ordered_named_pairs"
---
--- Prepared statements
---
-CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
-rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
-return rv[0]['val']
-$$;
-SELECT test_prep_bool_input(); -- 1
- test_prep_bool_input 
-----------------------
-                    1
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
-rv = plpy.execute(plan, [0], 5)
-plpy.info(rv[0])
-return rv[0]['val']
-$$;
-SELECT test_prep_bool_output(); -- false
-INFO:  {'val': False}
- test_prep_bool_output 
------------------------
- f
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
-rv = plpy.execute(plan, [bb], 5)
-return rv[0]['val']
-$$;
-SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
- test_prep_bytea_input 
------------------------
-                     3
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
-rv = plpy.execute(plan, [], 5)
-plpy.info(rv[0])
-return rv[0]['val']
-$$;
-SELECT test_prep_bytea_output();
-INFO:  {'val': b'\xaa\x00\xbb'}
- test_prep_bytea_output 
-------------------------
- \xaa00bb
-(1 row)
-
diff --git a/src/pl/plpython/expected/plpython_unicode.out b/src/pl/plpython/expected/plpython_unicode.out
index c7546dd4587..fd54b0b88e8 100644
--- a/src/pl/plpython/expected/plpython_unicode.out
+++ b/src/pl/plpython/expected/plpython_unicode.out
@@ -11,24 +11,24 @@ CREATE TABLE unicode_test (
 	testvalue  text NOT NULL
 );
 CREATE FUNCTION unicode_return() RETURNS text AS E'
-return u"\\xA0"
-' LANGUAGE plpythonu;
+return "\\xA0"
+' LANGUAGE plpython3u;
 CREATE FUNCTION unicode_trigger() RETURNS trigger AS E'
-TD["new"]["testvalue"] = u"\\xA0"
+TD["new"]["testvalue"] = "\\xA0"
 return "MODIFY"
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test
   FOR EACH ROW EXECUTE PROCEDURE unicode_trigger();
 CREATE FUNCTION unicode_plan1() RETURNS text AS E'
 plan = plpy.prepare("SELECT $1 AS testvalue", ["text"])
-rv = plpy.execute(plan, [u"\\xA0"], 1)
+rv = plpy.execute(plan, ["\\xA0"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 CREATE FUNCTION unicode_plan2() RETURNS text AS E'
-plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"])
+plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"])
 rv = plpy.execute(plan, ["foo", "bar"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 SELECT unicode_return();
  unicode_return 
 ----------------
diff --git a/src/pl/plpython/expected/plpython_void.out b/src/pl/plpython/expected/plpython_void.out
index 1080d12d6b2..07d0760783e 100644
--- a/src/pl/plpython/expected/plpython_void.out
+++ b/src/pl/plpython/expected/plpython_void.out
@@ -3,14 +3,14 @@
 --
 CREATE FUNCTION test_void_func1() RETURNS void AS $$
 x = 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- illegal: can't return non-None value in void-returning func
 CREATE FUNCTION test_void_func2() RETURNS void AS $$
 return 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_return_none() RETURNS int AS $$
 None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- Tests for functions returning void
 SELECT test_void_func1(), test_void_func1() IS NULL AS "is null";
  test_void_func1 | is null 
diff --git a/src/pl/plpython/regress-python3-mangle.mk b/src/pl/plpython/regress-python3-mangle.mk
deleted file mode 100644
index a785818a172..00000000000
--- a/src/pl/plpython/regress-python3-mangle.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-ifeq ($(python_majorversion),3)
-# Adjust regression tests for Python 3 compatibility
-#
-# Mention those regression test files that need to be mangled in the
-# variable REGRESS_PLPYTHON3_MANGLE.  They will be copied to a
-# subdirectory python3/ and have their Python syntax and other bits
-# adjusted to work with Python 3.
-
-# Note that the order of the tests needs to be preserved in this
-# expression.
-REGRESS := $(foreach test,$(REGRESS),$(if $(filter $(test),$(REGRESS_PLPYTHON3_MANGLE)),python3/$(test),$(test)))
-
-.PHONY: pgregress-python3-mangle
-pgregress-python3-mangle:
-	$(MKDIR_P) sql/python3 expected/python3 results/python3
-	for file in $(patsubst %,$(srcdir)/sql/%.sql,$(REGRESS_PLPYTHON3_MANGLE)) $(patsubst %,$(srcdir)/expected/%*.out,$(REGRESS_PLPYTHON3_MANGLE)); do \
-	  sed \
-	      -e "s/<type 'exceptions\.\([[:alpha:]]*\)'>/<class '\1'>/g" \
-	      -e "s/<type 'long'>/<class 'int'>/g" \
-	      -e "s/\([0-9][0-9]*\)L/\1/g" \
-	      -e 's/\([ [{]\)u"/\1"/g' \
-	      -e "s/\([ [{]\)u'/\1'/g" \
-	      -e "s/def next/def __next__/g" \
-	      -e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
-	      -e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
-	      -e "s/EXTENSION plpythonu/EXTENSION plpython3u/g" \
-	      -e "s/EXTENSION plpython2u/EXTENSION plpython3u/g" \
-	      -e "s/EXTENSION \([^ ]*\)_plpythonu/EXTENSION \1_plpython3u/g" \
-	      -e "s/EXTENSION \([^ ]*\)_plpython2u/EXTENSION \1_plpython3u/g" \
-	      -e 's/installing required extension "plpython2u"/installing required extension "plpython3u"/g' \
-	    $$file >`echo $$file | sed 's,^.*/\([^/][^/]*/\)\([^/][^/]*\)$$,\1python3/\2,'` || exit; \
-	done
-
-check installcheck: pgregress-python3-mangle
-
-pg_regress_clean_files += sql/python3/ expected/python3/ results/python3/
-
-endif # Python 3
diff --git a/src/pl/plpython/sql/plpython_call.sql b/src/pl/plpython/sql/plpython_call.sql
index b0b3705ae3c..daa4bc377d7 100644
--- a/src/pl/plpython/sql/plpython_call.sql
+++ b/src/pl/plpython/sql/plpython_call.sql
@@ -3,7 +3,7 @@
 --
 
 CREATE PROCEDURE test_proc1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 pass
 $$;
@@ -13,7 +13,7 @@ CALL test_proc1();
 
 -- error: can't return non-None
 CREATE PROCEDURE test_proc2()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return 5
 $$;
@@ -24,7 +24,7 @@ CALL test_proc2();
 CREATE TABLE test1 (a int);
 
 CREATE PROCEDURE test_proc3(x int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("INSERT INTO test1 VALUES (%s)" % x)
 $$;
@@ -37,7 +37,7 @@ SELECT * FROM test1;
 -- output arguments
 
 CREATE PROCEDURE test_proc5(INOUT a text)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return [a + '+' + a]
 $$;
@@ -46,7 +46,7 @@ CALL test_proc5('abc');
 
 
 CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return (b * a, c * a)
 $$;
@@ -57,7 +57,7 @@ CALL test_proc6(2, 3, 4);
 -- OUT parameters
 
 CREATE PROCEDURE test_proc9(IN a int, OUT b int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.notice("a: %s" % (a))
 return (a * 2,)
diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql
index 0fd2f5d5e3b..21757701cc1 100644
--- a/src/pl/plpython/sql/plpython_composite.sql
+++ b/src/pl/plpython/sql/plpython_composite.sql
@@ -1,6 +1,6 @@
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT multiout_simple();
 SELECT * FROM multiout_simple();
@@ -9,7 +9,7 @@ SELECT (multiout_simple()).j + 3;
 
 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT multiout_simple_setof();
 SELECT * FROM multiout_simple_setof();
@@ -34,7 +34,7 @@ elif typ == 'obj':
     return type_record
 elif typ == 'str':
     return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
 SELECT multiout_record_as('dict', 'foo', 1, 'f');
@@ -77,7 +77,7 @@ for i in range(n):
     power = 2 ** i
     length = plpy.execute("select length('%d')" % power)[0]['length']
     yield power, length
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_setof(3);
 SELECT multiout_setof(5);
@@ -86,7 +86,7 @@ CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
 return [{'x': 4, 'y' :'four'},
         {'x': 7, 'y' :'seven'},
         {'x': 0, 'y' :'zero'}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_return_table();
 
@@ -94,18 +94,18 @@ CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS
 yield [[1], 'a']
 yield [[1,2], 'b']
 yield [[1,2,3], None]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_array();
 
 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
 return {'first': 1, 'second': 2}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
 return [{'first': 1, 'second': 2},
        {'first': 3, 'second': 4	}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM singleout_composite();
 SELECT * FROM multiout_composite();
@@ -113,7 +113,7 @@ SELECT * FROM multiout_composite();
 -- composite OUT parameters in functions returning RECORD not supported yet
 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
 return (n, (n * 2, n * 3))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
 if returnnull:
@@ -132,7 +132,7 @@ elif typ == 'str':
     d = "(%r,%r)" % (n * 2, n * 3)
 for i in range(n):
     yield (i, d)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_composite(2);
 SELECT * FROM multiout_table_type_setof('dict', 'f', 3);
@@ -157,7 +157,7 @@ CREATE TABLE changing (
 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
 return [(1, {'i': 1, 'j': 2}),
         (1, (3, 4))]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM changing_test();
 ALTER TABLE changing DROP COLUMN j;
@@ -178,14 +178,14 @@ yield {'tab': [('first', 1), ('second', 2)],
 yield {'tab': [('first', 1), ('second', 2)],
       'typ': [{'first': 'third', 'second': 3},
               {'first': 'fourth', 'second': 4}]}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM composite_types_table();
 
 -- check what happens if the output record descriptor changes
 CREATE FUNCTION return_record(t text) RETURNS record AS $$
 return {'t': t, 'val': 10}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM return_record('abc') AS r(t text, val integer);
 SELECT * FROM return_record('abc') AS r(t text, val bigint);
@@ -196,7 +196,7 @@ SELECT * FROM return_record('999') AS r(val text, t integer);
 
 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
 return {'v1':1,'v2':2,t:3}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
 SELECT * FROM return_record_2('v3') AS (v2 int, v3 int, v1 int);
@@ -211,7 +211,7 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
 -- multi-dimensional array of composite types.
 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list();
 
 -- Starting with PostgreSQL 10, a composite type in an array cannot be
@@ -220,5 +220,5 @@ SELECT * FROM composite_type_as_list();
 -- on the issue.
 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
   return [['first', 1]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list_broken();
diff --git a/src/pl/plpython/sql/plpython_do.sql b/src/pl/plpython/sql/plpython_do.sql
index 0e281a08ee8..d49413268e9 100644
--- a/src/pl/plpython/sql/plpython_do.sql
+++ b/src/pl/plpython/sql/plpython_do.sql
@@ -1,5 +1,3 @@
-DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
+DO $$ plpy.notice("This is plpython3u.") $$ LANGUAGE plpython3u;
 
-DO $$ plpy.notice("This is plpython2u.") $$ LANGUAGE plpython2u;
-
-DO $$ raise Exception("error test") $$ LANGUAGE plpythonu;
+DO $$ raise Exception("error test") $$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/sql/plpython_drop.sql b/src/pl/plpython/sql/plpython_drop.sql
index 72d5d657ec3..e4f373b2bc7 100644
--- a/src/pl/plpython/sql/plpython_drop.sql
+++ b/src/pl/plpython/sql/plpython_drop.sql
@@ -3,6 +3,4 @@
 --
 SET client_min_messages = WARNING;
 
-DROP EXTENSION plpythonu CASCADE;
-
-DROP EXTENSION IF EXISTS plpython2u CASCADE;
+DROP EXTENSION plpython3u CASCADE;
diff --git a/src/pl/plpython/sql/plpython_ereport.sql b/src/pl/plpython/sql/plpython_ereport.sql
index 58df2057ef5..3bcf8f5cde9 100644
--- a/src/pl/plpython/sql/plpython_ereport.sql
+++ b/src/pl/plpython/sql/plpython_ereport.sql
@@ -17,28 +17,28 @@ plpy.info('This is message text.',
 plpy.notice('notice', detail='some detail')
 plpy.warning('warning', detail='some detail')
 plpy.error('stop on error', detail='some detail', hint='some hint')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT elog_test();
 
-DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpythonu;
+DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u;
 
 DO $$
 import time;
 from datetime import date
 plpy.info('other types', detail=date(2016, 2, 26))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 plpy.info('other types', detail=basket)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- should fail
-DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u;
 
 -- raise exception in python, handle exception in plgsql
 CREATE OR REPLACE FUNCTION raise_exception(_message text, _detail text DEFAULT NULL, _hint text DEFAULT NULL,
@@ -57,7 +57,7 @@ kwargs = {
 }
 # ignore None values
 plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT raise_exception('hello', 'world');
 SELECT raise_exception('message text', 'detail text', _sqlstate => 'YY333');
@@ -128,7 +128,7 @@ try:
 except Exception as e:
     plpy.info(e.spidata)
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 try:
@@ -136,4 +136,4 @@ try:
 except Exception as e:
     plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name))
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index 88d6936fd0d..11f14ec5a7c 100644
--- a/src/pl/plpython/sql/plpython_error.sql
+++ b/src/pl/plpython/sql/plpython_error.sql
@@ -7,7 +7,7 @@
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 /* With check_function_bodies = false the function should get defined
  * and the error reported when called
@@ -17,7 +17,7 @@ SET check_function_bodies = false;
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 SELECT python_syntax_error();
 /* Run the function twice to check if the hashtable entry gets cleaned up */
@@ -30,7 +30,7 @@ RESET check_function_bodies;
 CREATE FUNCTION sql_syntax_error() RETURNS text
         AS
 'plpy.execute("syntax error")'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 SELECT sql_syntax_error();
 
@@ -40,7 +40,7 @@ SELECT sql_syntax_error();
 CREATE FUNCTION exception_index_invalid(text) RETURNS text
 	AS
 'return args[1]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT exception_index_invalid('test');
 
@@ -51,7 +51,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text
 	AS
 'rv = plpy.execute("SELECT test5(''foo'')")
 return rv[0]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT exception_index_invalid_nested();
 
@@ -68,7 +68,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_uncaught('rick');
 
@@ -90,7 +90,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_caught('rick');
 
@@ -111,7 +111,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_reraised('rick');
 
@@ -127,7 +127,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT valid_type('rick');
 
@@ -147,7 +147,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_error();
 
@@ -167,7 +167,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_error_raise();
 
@@ -187,7 +187,7 @@ def fun3():
 fun3()
 return "you''ve been warned"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_warning();
 
@@ -196,7 +196,7 @@ SELECT nested_warning();
 CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
 $$
 plpy.nonexistent
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT toplevel_attribute_error();
 
@@ -213,7 +213,7 @@ def third():
   plpy.execute("select sql_error()")
 
 first()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
 begin
@@ -229,7 +229,7 @@ $$ LANGUAGE plpgsql;
 
 CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
 plpy.execute("select sql_error()")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT python_traceback();
 SELECT sql_error();
@@ -251,7 +251,7 @@ except spiexceptions.NotNullViolation as e:
     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
 except spiexceptions.UniqueViolation as e:
     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT specific_exception(2);
 SELECT specific_exception(NULL);
@@ -262,7 +262,7 @@ SELECT specific_exception(2);
 CREATE FUNCTION python_unique_violation() RETURNS void AS $$
 plpy.execute("insert into specific values (1)")
 plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
 begin
@@ -283,7 +283,7 @@ CREATE FUNCTION manual_subxact() RETURNS void AS $$
 plpy.execute("savepoint save")
 plpy.execute("create table foo(x integer)")
 plpy.execute("rollback to save")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT manual_subxact();
 
@@ -295,7 +295,7 @@ rollback = plpy.prepare("rollback to save")
 plpy.execute(save)
 plpy.execute("create table foo(x integer)")
 plpy.execute(rollback)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT manual_subxact_prepared();
 
@@ -303,7 +303,7 @@ SELECT manual_subxact_prepared();
  */
 CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
 raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 BEGIN
@@ -319,7 +319,7 @@ CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
 exc = plpy.spiexceptions.DivisionByZero()
 exc.sqlstate = 'SILLY'
 raise exc
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 BEGIN
@@ -332,14 +332,14 @@ $$ LANGUAGE plpgsql;
 /* test the context stack trace for nested execution levels
  */
 CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
 plpy.execute("SELECT notice_innerfunc()")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 \set SHOW_CONTEXT always
 
diff --git a/src/pl/plpython/sql/plpython_global.sql b/src/pl/plpython/sql/plpython_global.sql
index 32502b41eee..96d20492861 100644
--- a/src/pl/plpython/sql/plpython_global.sql
+++ b/src/pl/plpython/sql/plpython_global.sql
@@ -9,7 +9,7 @@ CREATE FUNCTION global_test_one() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_one"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 CREATE FUNCTION global_test_two() returns text
     AS
@@ -18,7 +18,7 @@ CREATE FUNCTION global_test_two() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_two"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 CREATE FUNCTION static_test() returns int4
@@ -29,7 +29,7 @@ else:
 	SD["call"] = 1
 return SD["call"]
 '
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 SELECT static_test();
diff --git a/src/pl/plpython/sql/plpython_import.sql b/src/pl/plpython/sql/plpython_import.sql
index ec887677e1e..3031eef2e69 100644
--- a/src/pl/plpython/sql/plpython_import.sql
+++ b/src/pl/plpython/sql/plpython_import.sql
@@ -7,7 +7,7 @@ CREATE FUNCTION import_fail() returns text
 except ImportError:
 	return "failed as expected"
 return "succeeded, that wasn''t supposed to happen"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 CREATE FUNCTION import_succeed() returns text
@@ -28,7 +28,7 @@ except Exception as ex:
 	plpy.notice("import failed -- %s" % str(ex))
 	return "failed, that wasn''t supposed to happen"
 return "succeeded, as expected"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 CREATE FUNCTION import_test_one(p text) RETURNS text
 	AS
@@ -39,7 +39,7 @@ except ImportError:
     import sha
     digest = sha.new(p)
 return digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION import_test_two(u users) RETURNS text
 	AS
@@ -51,7 +51,7 @@ except ImportError:
     import sha
     digest = sha.new(plain);
 return "sha hash of " + plain + " is " + digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 -- import python modules
diff --git a/src/pl/plpython/sql/plpython_newline.sql b/src/pl/plpython/sql/plpython_newline.sql
index f9cee9491bb..cb22ba923f9 100644
--- a/src/pl/plpython/sql/plpython_newline.sql
+++ b/src/pl/plpython/sql/plpython_newline.sql
@@ -4,15 +4,15 @@
 
 CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS
 E'x = 100\ny = 23\nreturn x + y\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS
 E'x = 100\ry = 23\rreturn x + y\r'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS
 E'x = 100\r\ny = 23\r\nreturn x + y\r\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 
 SELECT newline_lf();
diff --git a/src/pl/plpython/sql/plpython_params.sql b/src/pl/plpython/sql/plpython_params.sql
index ee75c4dc410..8bab4888592 100644
--- a/src/pl/plpython/sql/plpython_params.sql
+++ b/src/pl/plpython/sql/plpython_params.sql
@@ -4,13 +4,13 @@
 
 CREATE FUNCTION test_param_names0(integer, integer) RETURNS int AS $$
 return args[0] + args[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_param_names1(a0 integer, a1 text) RETURNS boolean AS $$
 assert a0 == args[0]
 assert a1 == args[1]
 return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_param_names2(u users) RETURNS text AS $$
 assert u == args[0]
@@ -22,7 +22,7 @@ if isinstance(u, dict):
 else:
     s = str(u)
 return s
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- use deliberately wrong parameter names
 CREATE FUNCTION test_param_names3(a0 integer) RETURNS boolean AS $$
@@ -32,7 +32,7 @@ try:
 except NameError as e:
 	assert e.args[0].find("a1") > -1
 	return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 SELECT test_param_names0(2,7);
diff --git a/src/pl/plpython/sql/plpython_quote.sql b/src/pl/plpython/sql/plpython_quote.sql
index 346b5485daf..a1133e7e266 100644
--- a/src/pl/plpython/sql/plpython_quote.sql
+++ b/src/pl/plpython/sql/plpython_quote.sql
@@ -9,7 +9,7 @@ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
         return plpy.quote_ident(t)
     else:
         raise plpy.Error("unrecognized quote type %s" % how)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT quote(t, 'literal') FROM (VALUES
        ('abc'),
diff --git a/src/pl/plpython/sql/plpython_record.sql b/src/pl/plpython/sql/plpython_record.sql
index 9bab4c9e82d..52bad8bccea 100644
--- a/src/pl/plpython/sql/plpython_record.sql
+++ b/src/pl/plpython/sql/plpython_record.sql
@@ -27,7 +27,7 @@ elif typ == 'obj':
 	type_record.first = first
 	type_record.second = second
 	return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
 if retnull:
@@ -45,20 +45,20 @@ elif typ == 'obj':
 	return type_record
 elif typ == 'str':
 	return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
 return first + '_in_to_out';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 -- Test tuple returning functions
@@ -136,14 +136,14 @@ SELECT * FROM test_type_record_as('obj', 'one', 1, false);
 
 CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
     return { 'first': 'first' }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error1();
 
 
 CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
     return [ 'first' ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error2();
 
@@ -152,12 +152,12 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
     class type_record: pass
     type_record.first = 'first'
     return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error3();
 
 CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
     return 'foo'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error4();
diff --git a/src/pl/plpython/sql/plpython_setof.sql b/src/pl/plpython/sql/plpython_setof.sql
index 16c2eef0ad6..4cfb10192c0 100644
--- a/src/pl/plpython/sql/plpython_setof.sql
+++ b/src/pl/plpython/sql/plpython_setof.sql
@@ -4,21 +4,21 @@
 
 CREATE FUNCTION test_setof_error() RETURNS SETOF text AS $$
 return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_setof_error();
 
 
 CREATE FUNCTION test_setof_as_list(count integer, content text) RETURNS SETOF text AS $$
 return [ content ]*count
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_as_tuple(count integer, content text) RETURNS SETOF text AS $$
 t = ()
 for i in range(count):
 	t += ( content, )
 return t
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_as_iterator(count integer, content text) RETURNS SETOF text AS $$
 class producer:
@@ -27,13 +27,13 @@ class producer:
 		self.icount = icount
 	def __iter__ (self):
 		return self
-	def next (self):
+	def __next__ (self):
 		if self.icount == 0:
 			raise StopIteration
 		self.icount -= 1
 		return self.icontent
 return producer(count, content)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_spi_in_iterator() RETURNS SETOF text AS
 $$
@@ -42,7 +42,7 @@ $$
         yield s
         plpy.execute('select 2')
 $$
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 
 -- Test set returning functions
@@ -69,7 +69,7 @@ global x
 while x <= lim:
     yield x
     x = x + 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT ugly(1, 5);
 
@@ -81,7 +81,7 @@ CREATE OR REPLACE FUNCTION get_user_records()
 RETURNS SETOF users
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT get_user_records();
 SELECT * FROM get_user_records();
@@ -91,7 +91,7 @@ CREATE OR REPLACE FUNCTION get_user_records2()
 RETURNS TABLE(fname text, lname text, username text, userid int)
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT get_user_records2();
 SELECT * FROM get_user_records2();
diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql
index dd77833ed56..112add93fc9 100644
--- a/src/pl/plpython/sql/plpython_spi.sql
+++ b/src/pl/plpython/sql/plpython_spi.sql
@@ -7,19 +7,19 @@ CREATE FUNCTION nested_call_one(a text) RETURNS text
 'q = "SELECT nested_call_two(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 CREATE FUNCTION nested_call_two(a text) RETURNS text
 	AS
 'q = "SELECT nested_call_three(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 CREATE FUNCTION nested_call_three(a text) RETURNS text
 	AS
 'return a'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 -- some spi stuff
 
@@ -35,7 +35,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
 	AS
@@ -49,7 +49,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
 	AS
@@ -64,7 +64,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION join_sequences(s sequences) RETURNS text
 	AS
@@ -77,7 +77,7 @@ for r in rv:
 	seq = seq + r["sequence"]
 return seq
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
 	AS
@@ -86,7 +86,7 @@ if a > 1:
     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
 return a + r
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 --
 -- spi and nested calls
@@ -120,7 +120,7 @@ if result.status() > 0:
    return result.nrows()
 else:
    return None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
 SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
@@ -129,7 +129,7 @@ CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return result.nrows()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_nrows_test($$SELECT 1$$);
 SELECT result_nrows_test($$CREATE TEMPORARY TABLE foo2 (a int, b text)$$);
@@ -140,7 +140,7 @@ CREATE FUNCTION result_len_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return len(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_len_test($$SELECT 1$$);
 SELECT result_len_test($$CREATE TEMPORARY TABLE foo3 (a int, b text)$$);
@@ -170,7 +170,7 @@ except TypeError:
 else:
     assert False, "TypeError not raised"
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_subscript_test();
 
@@ -180,7 +180,7 @@ result = plpy.execute("select 1 where false")
 
 plpy.info(result[:])
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_empty_test();
 
@@ -189,7 +189,7 @@ AS $$
 plan = plpy.prepare(cmd)
 result = plpy.execute(plan)
 return str(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
 SELECT result_str_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
@@ -203,13 +203,13 @@ for row in res:
     if row['lname'] == 'doe':
         does += 1
 return does
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
 res.close()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -228,7 +228,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users order by fname")
@@ -242,7 +242,7 @@ except AttributeError:
 assert item['fname'] == 'rick'
 
 assert len(res.fetch(2)) == 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -253,7 +253,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION next_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -267,7 +267,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users where false")
@@ -281,7 +281,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
 plan = plpy.prepare(
@@ -291,13 +291,13 @@ for row in plpy.cursor(plan, ["w"]):
     yield row['fname']
 for row in plan.cursor(["j"]):
     yield row['fname']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
                     ["text"])
 c = plpy.cursor(plan, ["a", "b"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TYPE test_composite_type AS (
   a1 int,
@@ -308,7 +308,7 @@ CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS
 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
 return res[0]["c1"]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT simple_cursor_test();
 SELECT double_cursor_close();
diff --git a/src/pl/plpython/sql/plpython_subtransaction.sql b/src/pl/plpython/sql/plpython_subtransaction.sql
index cc4b1ae102b..c65c380f40c 100644
--- a/src/pl/plpython/sql/plpython_subtransaction.sql
+++ b/src/pl/plpython/sql/plpython_subtransaction.sql
@@ -17,7 +17,7 @@ with plpy.subtransaction():
         plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
     elif what_error == "Python":
         raise Exception("Python exception")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_ctx_test();
 SELECT * FROM subtransaction_tbl;
@@ -45,7 +45,7 @@ with plpy.subtransaction():
             raise
         plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_nested_test();
 SELECT * FROM subtransaction_tbl;
@@ -65,7 +65,7 @@ with plpy.subtransaction():
     plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
     plpy.execute("SELECT subtransaction_nested_test('t')")
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_deeply_nested_test();
 SELECT * FROM subtransaction_tbl;
@@ -76,25 +76,25 @@ TRUNCATE subtransaction_tbl;
 CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
 AS $$
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__exit__(None, None, None)
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
 AS $$
@@ -102,7 +102,7 @@ s = plpy.subtransaction()
 s.__enter__()
 s.__exit__(None, None, None)
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
 AS $$
@@ -110,14 +110,14 @@ s = plpy.subtransaction()
 s.__enter__()
 s.__enter__()
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- No warnings here, as the subtransaction gets indeed closed
 CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
 AS $$
 with plpy.subtransaction() as s:
     s.__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
 AS $$
@@ -126,7 +126,7 @@ try:
         s.__exit__(None, None, None)
 except ValueError as e:
     raise ValueError(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_exit_without_enter();
 SELECT subtransaction_enter_without_exit();
@@ -159,7 +159,7 @@ try:
     plpy.execute(p, ["wrong"])
 except plpy.SPIError:
     plpy.warning("Caught a SPI error")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_mix_explicit_and_implicit();
 SELECT * FROM subtransaction_tbl;
@@ -172,7 +172,7 @@ AS $$
 s = plpy.subtransaction()
 s.enter()
 s.exit(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_alternative_names();
 
@@ -186,7 +186,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT try_catch_inside_subtransaction();
 SELECT * FROM subtransaction_tbl;
@@ -202,7 +202,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT pk_violation_inside_subtransaction();
 SELECT * FROM subtransaction_tbl;
@@ -217,7 +217,7 @@ with plpy.subtransaction():
     cur.fetch(10)
 fetched = cur.fetch(10);
 return int(fetched[5]["i"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
 try:
@@ -229,7 +229,7 @@ except plpy.SPIError:
     fetched = cur.fetch(10)
     return int(fetched[5]["i"])
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
 try:
@@ -243,7 +243,7 @@ except plpy.SPIError:
     fetched = cur.fetch(5)
     return fetched[2]["i"]
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
 try:
@@ -254,7 +254,7 @@ except plpy.SPIError:
     cur.close()
     return True
 return False # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT cursor_in_subxact();
 SELECT cursor_aborted_subxact();
diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql
index 5f1be9c94a8..aa22a274155 100644
--- a/src/pl/plpython/sql/plpython_test.sql
+++ b/src/pl/plpython/sql/plpython_test.sql
@@ -1,13 +1,13 @@
 -- first some tests of basic functionality
-CREATE EXTENSION plpython2u;
+CREATE EXTENSION plpython3u;
 
 -- really stupid function just to get the module loaded
-CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
+CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 
 select stupid();
 
 -- check 2/3 versioning
-CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
+CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 
 select stupidn();
 
@@ -21,7 +21,7 @@ for key in keys:
     out.append("%s: %s" % (key, u[key]))
 words = a1 + " " + a2 + " => {" + ", ".join(out) + "}"
 return words'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 select "Argument test #1"(users, fname, lname) from users where lname = 'doe' order by 1;
 
@@ -32,7 +32,7 @@ $$
 contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
 contents.sort()
 return contents
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select module_contents();
 
@@ -47,6 +47,6 @@ plpy.info('info', 37, [1, 2, 3])
 plpy.notice('notice')
 plpy.warning('warning')
 plpy.error('error')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT elog_test_basic();
diff --git a/src/pl/plpython/sql/plpython_transaction.sql b/src/pl/plpython/sql/plpython_transaction.sql
index 33b37e5b7fc..f9062254572 100644
--- a/src/pl/plpython/sql/plpython_transaction.sql
+++ b/src/pl/plpython/sql/plpython_transaction.sql
@@ -2,7 +2,7 @@ CREATE TABLE test1 (a int, b text);
 
 
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -20,7 +20,7 @@ SELECT * FROM test1;
 TRUNCATE test1;
 
 DO
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -37,7 +37,7 @@ TRUNCATE test1;
 
 -- not allowed in a function
 CREATE FUNCTION transaction_test2() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -55,7 +55,7 @@ SELECT * FROM test1;
 
 -- also not allowed if procedure is called from a function
 CREATE FUNCTION transaction_test3() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("CALL transaction_test1()")
 return 1
@@ -68,9 +68,9 @@ SELECT * FROM test1;
 
 -- DO block inside function
 CREATE FUNCTION transaction_test4() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 return 1
 $$;
 
@@ -78,7 +78,7 @@ SELECT transaction_test4();
 
 
 -- commit inside subtransaction (prohibited)
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 s = plpy.subtransaction()
 s.enter()
 plpy.commit()
@@ -91,7 +91,7 @@ INSERT INTO test2 VALUES (0), (1), (2), (3), (4);
 
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.commit()
@@ -106,7 +106,7 @@ SELECT * FROM pg_cursors;
 -- error in cursor loop with commit
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x'])
     plpy.commit()
@@ -120,7 +120,7 @@ SELECT * FROM pg_cursors;
 -- rollback inside cursor loop
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.rollback()
@@ -134,7 +134,7 @@ SELECT * FROM pg_cursors;
 -- first commit then rollback inside cursor loop
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     if row['x'] % 2 == 0:
diff --git a/src/pl/plpython/sql/plpython_trigger.sql b/src/pl/plpython/sql/plpython_trigger.sql
index 19852dc5851..e5504b9ab1d 100644
--- a/src/pl/plpython/sql/plpython_trigger.sql
+++ b/src/pl/plpython/sql/plpython_trigger.sql
@@ -16,7 +16,7 @@ if TD["new"]["fname"] == "william":
 	TD["new"]["fname"] = TD["args"][0]
 	rv = "MODIFY"
 return rv'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE FUNCTION users_update() returns trigger
@@ -25,7 +25,7 @@ CREATE FUNCTION users_update() returns trigger
 	if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
 		return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE FUNCTION users_delete() RETURNS trigger
@@ -33,7 +33,7 @@ CREATE FUNCTION users_delete() RETURNS trigger
 'if TD["old"]["fname"] == TD["args"][0]:
 	return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
@@ -72,7 +72,7 @@ CREATE TABLE trigger_test_generated (
         j int GENERATED ALWAYS AS (i * 2) STORED
 );
 
-CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
 
 if 'relid' in TD:
 	TD['relid'] = "bogus:12345"
@@ -157,7 +157,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid1() RETURNS trigger
 AS $$
     return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger1
 BEFORE INSERT ON trigger_test
@@ -173,7 +173,7 @@ DROP TRIGGER stupid_trigger1 ON trigger_test;
 CREATE FUNCTION stupid2() RETURNS trigger
 AS $$
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger2
 BEFORE DELETE ON trigger_test
@@ -191,7 +191,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid3() RETURNS trigger
 AS $$
     return "foo"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
@@ -206,8 +206,8 @@ DROP TRIGGER stupid_trigger3 ON trigger_test;
 
 CREATE FUNCTION stupid3u() RETURNS trigger
 AS $$
-    return u"foo"
-$$ LANGUAGE plpythonu;
+    return "foo"
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
@@ -224,7 +224,7 @@ CREATE FUNCTION stupid4() RETURNS trigger
 AS $$
     del TD["new"]
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger4
 BEFORE UPDATE ON trigger_test
@@ -241,7 +241,7 @@ CREATE FUNCTION stupid5() RETURNS trigger
 AS $$
     TD["new"] = ['foo', 'bar']
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger5
 BEFORE UPDATE ON trigger_test
@@ -258,7 +258,7 @@ CREATE FUNCTION stupid6() RETURNS trigger
 AS $$
     TD["new"] = {1: 'foo', 2: 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger6
 BEFORE UPDATE ON trigger_test
@@ -275,7 +275,7 @@ CREATE FUNCTION stupid7() RETURNS trigger
 AS $$
     TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
@@ -290,9 +290,9 @@ DROP TRIGGER stupid_trigger7 ON trigger_test;
 
 CREATE FUNCTION stupid7u() RETURNS trigger
 AS $$
-    TD["new"] = {u'v': 'foo', u'a': 'bar'}
+    TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
@@ -318,7 +318,7 @@ CREATE FUNCTION test_null() RETURNS trigger
 AS $$
     TD["new"]['v'] = None
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER test_null_trigger
 BEFORE UPDATE ON trigger_test
@@ -341,7 +341,7 @@ SET DateStyle = 'ISO';
 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
 
@@ -365,7 +365,7 @@ CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
     TD['new']['f1'] = (3, False)
     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
@@ -380,7 +380,7 @@ CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
 
 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
@@ -399,7 +399,7 @@ CREATE TABLE composite_trigger_nested_test(c comp3);
 
 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
@@ -410,7 +410,7 @@ INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'
 SELECT * FROM composite_trigger_nested_test;
 
 -- check that using a function as a trigger over two tables works correctly
-CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
     TD["new"]["data"] = '1234'
     return 'MODIFY'
 $$;
@@ -432,7 +432,7 @@ SELECT * FROM b;
 CREATE TABLE transition_table_test (id int, name text);
 INSERT INTO transition_table_test VALUES (1, 'a');
 
-CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
 $$
     rv = plpy.execute("SELECT * FROM old_table")
     assert(rv.nrows() == 1)
@@ -455,7 +455,7 @@ DROP FUNCTION transition_table_test_f();
 -- dealing with generated columns
 
 CREATE FUNCTION generated_test_func1() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 TD['new']['j'] = 5  # not allowed
 return 'MODIFY'
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
index 0d207d9c015..40f4f79d99f 100644
--- a/src/pl/plpython/sql/plpython_types.sql
+++ b/src/pl/plpython/sql/plpython_types.sql
@@ -9,7 +9,7 @@
 CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bool(true);
 SELECT * FROM test_type_conversion_bool(false);
@@ -35,7 +35,7 @@ elif n == 5:
    ret = [0]
 plpy.info(ret, not not ret)
 return ret
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bool_other(0);
 SELECT * FROM test_type_conversion_bool_other(1);
@@ -48,7 +48,7 @@ SELECT * FROM test_type_conversion_bool_other(5);
 CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_char('a');
 SELECT * FROM test_type_conversion_char(null);
@@ -57,7 +57,7 @@ SELECT * FROM test_type_conversion_char(null);
 CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int2(100::int2);
 SELECT * FROM test_type_conversion_int2(-100::int2);
@@ -67,7 +67,7 @@ SELECT * FROM test_type_conversion_int2(null);
 CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int4(100);
 SELECT * FROM test_type_conversion_int4(-100);
@@ -77,7 +77,7 @@ SELECT * FROM test_type_conversion_int4(null);
 CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int8(100);
 SELECT * FROM test_type_conversion_int8(-100);
@@ -90,7 +90,7 @@ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
 # between decimal and cdecimal
 plpy.info(str(x), x.__class__.__name__)
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_numeric(100);
 SELECT * FROM test_type_conversion_numeric(-100);
@@ -105,7 +105,7 @@ SELECT * FROM test_type_conversion_numeric(null);
 CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_float4(100);
 SELECT * FROM test_type_conversion_float4(-100);
@@ -116,7 +116,7 @@ SELECT * FROM test_type_conversion_float4(null);
 CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_float8(100);
 SELECT * FROM test_type_conversion_float8(-100);
@@ -128,7 +128,7 @@ SELECT * FROM test_type_conversion_float8(100100100.654321);
 CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_oid(100);
 SELECT * FROM test_type_conversion_oid(2147483649);
@@ -138,7 +138,7 @@ SELECT * FROM test_type_conversion_oid(null);
 CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_text('hello world');
 SELECT * FROM test_type_conversion_text(null);
@@ -147,7 +147,7 @@ SELECT * FROM test_type_conversion_text(null);
 CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bytea('hello world');
 SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
@@ -157,7 +157,7 @@ SELECT * FROM test_type_conversion_bytea(null);
 CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
 import marshal
 return marshal.dumps('hello world')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
 import marshal
@@ -165,7 +165,7 @@ try:
     return marshal.loads(x)
 except ValueError as e:
     return 'FAILED: ' + str(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
 
@@ -178,7 +178,7 @@ CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
 
 CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_booltrue(true, true);
 SELECT * FROM test_type_conversion_booltrue(false, true);
@@ -190,7 +190,7 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
 CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
 SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
@@ -201,7 +201,7 @@ CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
 
 CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_nnint(10, 20);
 SELECT * FROM test_type_conversion_nnint(null, 20);
@@ -213,7 +213,7 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
 CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
 SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
@@ -229,7 +229,7 @@ SELECT * FROM test_type_conversion_bytea10('hello word', null);
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
@@ -243,14 +243,14 @@ SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
 CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
 
 CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
             [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
@@ -258,7 +258,7 @@ SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',
 CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
             [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
@@ -270,7 +270,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemint4(8,5,3,2);
 
@@ -278,7 +278,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemint8(5,5,3,2);
 
@@ -286,7 +286,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemfloat4(6,5,3,2);
 
@@ -294,14 +294,14 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemfloat8(7,5,3,2);
 
 CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
 SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
@@ -310,59 +310,59 @@ SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar
 CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
 
 
 CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_mixed1();
 
 
 CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_mixed2();
 
 CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
 return [[1,2,3],[4,5]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_mdarray_malformed();
 
 CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
 return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_mdarray_toodeep();
 
 
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_record();
 
 
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_string();
 
 CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
 return ('abc', 'def')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_tuple();
 
 CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
 return 5
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_error();
 
@@ -376,14 +376,14 @@ CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AN
 CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
 SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
 
 CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
 return [2,1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain_check_violation();
 
 
@@ -394,13 +394,13 @@ SELECT * FROM test_type_conversion_array_domain_check_violation();
 CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_read_uint2_array(array[1::uint2]);
 
 CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_build_uint2_array(1::int2);
 select test_build_uint2_array(-1::int2);  -- fail
@@ -413,7 +413,7 @@ select test_build_uint2_array(-1::int2);  -- fail
 CREATE FUNCTION test_type_conversion_domain_array(x integer[])
   RETURNS ordered_pair_domain[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_domain_array(array[2,4]);
 select test_type_conversion_domain_array(array[4,2]);  -- fail
@@ -422,7 +422,7 @@ CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
   RETURNS integer AS $$
 plpy.info(x, type(x))
 return x[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_domain_array2(array[2,4]);
 select test_type_conversion_domain_array2(array[4,2]);  -- fail
@@ -431,7 +431,7 @@ CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
   RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
 
@@ -450,7 +450,7 @@ INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
 
 CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
 return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT name, test_composite_table_input(employee.*) FROM employee;
 
@@ -470,7 +470,7 @@ CREATE TYPE named_pair AS (
 
 CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
 return sum(p.values())
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_composite_type_input(row(1, 2));
 
@@ -487,7 +487,7 @@ CREATE TYPE nnint_container AS (f1 int, f2 nnint);
 
 CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
 return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT nnint_test(null, 3);
 SELECT nnint_test(3, null);  -- fail
@@ -501,21 +501,21 @@ CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
 
 CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
 return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT read_ordered_named_pair(row(1, 2));
 SELECT read_ordered_named_pair(row(2, 1));  -- fail
 
 CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
 return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT build_ordered_named_pair(1,2);
 SELECT build_ordered_named_pair(2,1);  -- fail
 
 CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
 return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT build_ordered_named_pairs(1,2);
 SELECT build_ordered_named_pairs(2,1);  -- fail
@@ -526,7 +526,7 @@ SELECT build_ordered_named_pairs(2,1);  -- fail
 --
 
 CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
 rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
@@ -537,7 +537,7 @@ SELECT test_prep_bool_input(); -- 1
 
 
 CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
 rv = plpy.execute(plan, [0], 5)
@@ -549,7 +549,7 @@ SELECT test_prep_bool_output(); -- false
 
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
 rv = plpy.execute(plan, [bb], 5)
@@ -560,7 +560,7 @@ SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated
 
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
 rv = plpy.execute(plan, [], 5)
diff --git a/src/pl/plpython/sql/plpython_unicode.sql b/src/pl/plpython/sql/plpython_unicode.sql
index a11e5eeaa21..14f7b4e0053 100644
--- a/src/pl/plpython/sql/plpython_unicode.sql
+++ b/src/pl/plpython/sql/plpython_unicode.sql
@@ -14,28 +14,28 @@ CREATE TABLE unicode_test (
 );
 
 CREATE FUNCTION unicode_return() RETURNS text AS E'
-return u"\\xA0"
-' LANGUAGE plpythonu;
+return "\\xA0"
+' LANGUAGE plpython3u;
 
 CREATE FUNCTION unicode_trigger() RETURNS trigger AS E'
-TD["new"]["testvalue"] = u"\\xA0"
+TD["new"]["testvalue"] = "\\xA0"
 return "MODIFY"
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test
   FOR EACH ROW EXECUTE PROCEDURE unicode_trigger();
 
 CREATE FUNCTION unicode_plan1() RETURNS text AS E'
 plan = plpy.prepare("SELECT $1 AS testvalue", ["text"])
-rv = plpy.execute(plan, [u"\\xA0"], 1)
+rv = plpy.execute(plan, ["\\xA0"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 CREATE FUNCTION unicode_plan2() RETURNS text AS E'
-plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"])
+plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"])
 rv = plpy.execute(plan, ["foo", "bar"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 
 SELECT unicode_return();
diff --git a/src/pl/plpython/sql/plpython_void.sql b/src/pl/plpython/sql/plpython_void.sql
index 77d7f59e4c7..5a1a6711fb0 100644
--- a/src/pl/plpython/sql/plpython_void.sql
+++ b/src/pl/plpython/sql/plpython_void.sql
@@ -4,16 +4,16 @@
 
 CREATE FUNCTION test_void_func1() RETURNS void AS $$
 x = 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- illegal: can't return non-None value in void-returning func
 CREATE FUNCTION test_void_func2() RETURNS void AS $$
 return 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_return_none() RETURNS int AS $$
 None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 -- Tests for functions returning void
diff --git a/contrib/hstore_plpython/.gitignore b/contrib/hstore_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/hstore_plpython/.gitignore
+++ b/contrib/hstore_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile
index 19d99a8045e..9d88cda1d0f 100644
--- a/contrib/hstore_plpython/Makefile
+++ b/contrib/hstore_plpython/Makefile
@@ -10,7 +10,6 @@ EXTENSION = hstore_plpython3u
 DATA = hstore_plpython3u--1.0.sql
 
 REGRESS = hstore_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 PG_CPPFLAGS = $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
@@ -37,9 +36,4 @@ SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
 
 REGRESS_OPTS += --load-extension=hstore
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=hstore_plpythonu
-endif
 EXTRA_INSTALL += contrib/hstore
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out
index ecf1dd61bc1..bf238701fec 100644
--- a/contrib/hstore_plpython/expected/hstore_plpython.out
+++ b/contrib/hstore_plpython/expected/hstore_plpython.out
@@ -1,8 +1,8 @@
-CREATE EXTENSION hstore_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION hstore_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 -- test hstore -> python
 CREATE FUNCTION test1(val hstore) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -18,7 +18,7 @@ INFO:  [('aa', 'bb'), ('cc', None)]
 
 -- the same with the versioned language name
 CREATE FUNCTION test1n(val hstore) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -34,7 +34,7 @@ INFO:  [('aa', 'bb'), ('cc', None)]
 
 -- test hstore[] -> python
 CREATE FUNCTION test1arr(val hstore[]) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}])
@@ -48,7 +48,7 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']);
 
 -- test python -> hstore
 CREATE FUNCTION test2(a int, b text) RETURNS hstore
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = {'a': a, 'b': b, 'c': None}
@@ -65,14 +65,14 @@ SELECT test2(1, 'boo');
 CREATE OR REPLACE FUNCTION public.test2(a integer, b text)
  RETURNS hstore
  TRANSFORM FOR TYPE hstore
- LANGUAGE plpythonu
+ LANGUAGE plpython3u
 AS $function$
 val = {'a': a, 'b': b, 'c': None}
 return val
 $function$
 -- test python -> hstore[]
 CREATE FUNCTION test2arr() RETURNS hstore[]
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
@@ -87,7 +87,7 @@ SELECT test2arr();
 -- test python -> domain over hstore
 CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
 CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 return {'a': 1, fn: 'boo', 'c': None}
@@ -104,7 +104,7 @@ CONTEXT:  while creating return value
 PL/Python function "test2dom"
 -- test as part of prepare/execute
 CREATE FUNCTION test3() RETURNS void
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1")
@@ -131,7 +131,7 @@ SELECT * FROM test1;
 (1 row)
 
 CREATE FUNCTION test4() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}})
diff --git a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql b/contrib/hstore_plpython/hstore_plpython2u--1.0.sql
deleted file mode 100644
index 800765f3f0c..00000000000
--- a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/hstore_plpython/hstore_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION hstore_plpython2u" to load this file. \quit
-
-CREATE FUNCTION hstore_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'hstore_to_plpython';
-
-CREATE FUNCTION plpython2_to_hstore(val internal) RETURNS hstore
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'plpython_to_hstore';
-
-CREATE TRANSFORM FOR hstore LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION hstore_to_plpython2(internal),
-    TO SQL WITH FUNCTION plpython2_to_hstore(internal)
-);
-
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython2u IS 'transform between hstore and Python dict';
diff --git a/contrib/hstore_plpython/hstore_plpython2u.control b/contrib/hstore_plpython/hstore_plpython2u.control
deleted file mode 100644
index ed905671123..00000000000
--- a/contrib/hstore_plpython/hstore_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# hstore_plpython2u extension
-comment = 'transform between hstore and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/hstore_plpython2'
-relocatable = true
-requires = 'hstore,plpython2u'
diff --git a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql b/contrib/hstore_plpython/hstore_plpythonu--1.0.sql
deleted file mode 100644
index 52832912abc..00000000000
--- a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/hstore_plpython/hstore_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION hstore_plpythonu" to load this file. \quit
-
-CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR hstore LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION hstore_to_plpython(internal),
-    TO SQL WITH FUNCTION plpython_to_hstore(internal)
-);
-
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'transform between hstore and Python dict';
diff --git a/contrib/hstore_plpython/hstore_plpythonu.control b/contrib/hstore_plpython/hstore_plpythonu.control
deleted file mode 100644
index 8e9b35e43bf..00000000000
--- a/contrib/hstore_plpython/hstore_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# hstore_plpythonu extension
-comment = 'transform between hstore and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/hstore_plpython2'
-relocatable = true
-requires = 'hstore,plpythonu'
diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql
index b6d98b7dd53..a9cfbbe13e2 100644
--- a/contrib/hstore_plpython/sql/hstore_plpython.sql
+++ b/contrib/hstore_plpython/sql/hstore_plpython.sql
@@ -1,9 +1,9 @@
-CREATE EXTENSION hstore_plpython2u CASCADE;
+CREATE EXTENSION hstore_plpython3u CASCADE;
 
 
 -- test hstore -> python
 CREATE FUNCTION test1(val hstore) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -16,7 +16,7 @@ SELECT test1('aa=>bb, cc=>NULL'::hstore);
 
 -- the same with the versioned language name
 CREATE FUNCTION test1n(val hstore) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -29,7 +29,7 @@ SELECT test1n('aa=>bb, cc=>NULL'::hstore);
 
 -- test hstore[] -> python
 CREATE FUNCTION test1arr(val hstore[]) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}])
@@ -41,7 +41,7 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']);
 
 -- test python -> hstore
 CREATE FUNCTION test2(a int, b text) RETURNS hstore
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = {'a': a, 'b': b, 'c': None}
@@ -56,7 +56,7 @@ SELECT test2(1, 'boo');
 
 -- test python -> hstore[]
 CREATE FUNCTION test2arr() RETURNS hstore[]
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
@@ -70,7 +70,7 @@ SELECT test2arr();
 CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
 
 CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 return {'a': 1, fn: 'boo', 'c': None}
@@ -82,7 +82,7 @@ SELECT test2dom('bar');  -- fail
 
 -- test as part of prepare/execute
 CREATE FUNCTION test3() RETURNS void
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1")
@@ -103,7 +103,7 @@ INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL');
 SELECT * FROM test1;
 
 CREATE FUNCTION test4() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}})
diff --git a/contrib/jsonb_plpython/.gitignore b/contrib/jsonb_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/jsonb_plpython/.gitignore
+++ b/contrib/jsonb_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/jsonb_plpython/Makefile b/contrib/jsonb_plpython/Makefile
index ff1c1019f1f..6333ea0bbaf 100644
--- a/contrib/jsonb_plpython/Makefile
+++ b/contrib/jsonb_plpython/Makefile
@@ -12,7 +12,6 @@ EXTENSION = jsonb_plpython3u
 DATA = jsonb_plpython3u--1.0.sql
 
 REGRESS = jsonb_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -33,9 +32,3 @@ else
 rpathdir = $(python_libdir)
 SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
-
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=jsonb_plpythonu
-endif
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/jsonb_plpython/expected/jsonb_plpython.out b/contrib/jsonb_plpython/expected/jsonb_plpython.out
index b491fe9cc68..cac963de69c 100644
--- a/contrib/jsonb_plpython/expected/jsonb_plpython.out
+++ b/contrib/jsonb_plpython/expected/jsonb_plpython.out
@@ -1,8 +1,8 @@
-CREATE EXTENSION jsonb_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION jsonb_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 -- test jsonb -> python dict
 CREATE FUNCTION test1(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -18,7 +18,7 @@ SELECT test1('{"a": 1, "c": "NULL"}'::jsonb);
 -- test jsonb -> python dict
 -- complex dict with dicts as value
 CREATE FUNCTION test1complex(val jsonb) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -34,7 +34,7 @@ SELECT test1complex('{"d": {"d": 1}}'::jsonb);
 -- test jsonb[] -> python dict
 -- dict with array as value
 CREATE FUNCTION test1arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -50,7 +50,7 @@ SELECT test1arr('{"d":[12, 1]}'::jsonb);
 -- test jsonb[] -> python list
 -- simple list
 CREATE FUNCTION test2arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -66,7 +66,7 @@ SELECT test2arr('[12, 1]'::jsonb);
 -- test jsonb[] -> python list
 -- array of dicts
 CREATE FUNCTION test3arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -81,7 +81,7 @@ SELECT test3arr('[{"a": 1, "b": 2}, {"c": 3,"d": 4}]'::jsonb);
 
 -- test jsonb int -> python int
 CREATE FUNCTION test1int(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == 1)
@@ -95,7 +95,7 @@ SELECT test1int('1'::jsonb);
 
 -- test jsonb string -> python string
 CREATE FUNCTION test1string(val jsonb) RETURNS text
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == "a")
@@ -109,7 +109,7 @@ SELECT test1string('"a"'::jsonb);
 
 -- test jsonb null -> python None
 CREATE FUNCTION test1null(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == None)
@@ -123,7 +123,7 @@ SELECT test1null('null'::jsonb);
 
 -- test python -> jsonb
 CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 as $$
 return val
@@ -238,7 +238,7 @@ SELECT roundtrip('["string", "string2"]'::jsonb);
 
 -- complex numbers -> jsonb
 CREATE FUNCTION testComplexNumbers() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 1 + 2j
@@ -250,7 +250,7 @@ CONTEXT:  while creating return value
 PL/Python function "testcomplexnumbers"
 -- range -> jsonb
 CREATE FUNCTION testRange() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = range(3)
@@ -264,7 +264,7 @@ SELECT testRange();
 
 -- 0xff -> jsonb
 CREATE FUNCTION testDecimal() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 0xff
@@ -278,7 +278,7 @@ SELECT testDecimal();
 
 -- tuple -> jsonb
 CREATE FUNCTION testTuple() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = (1, 'String', None)
@@ -292,7 +292,7 @@ SELECT testTuple();
 
 -- interesting dict -> jsonb
 CREATE FUNCTION test_dict1() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = {"a": 1, None: 2, 33: 3}
diff --git a/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql b/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
deleted file mode 100644
index 2526d14ee19..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION jsonb_plpython2u" to load this file. \quit
-
-CREATE FUNCTION jsonb_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'jsonb_to_plpython';
-
-CREATE FUNCTION plpython2_to_jsonb(val internal) RETURNS jsonb
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'plpython_to_jsonb';
-
-CREATE TRANSFORM FOR jsonb LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION jsonb_to_plpython2(internal),
-    TO SQL WITH FUNCTION plpython2_to_jsonb(internal)
-);
-
-COMMENT ON TRANSFORM FOR jsonb LANGUAGE plpython2u IS 'transform between jsonb and Python';
diff --git a/contrib/jsonb_plpython/jsonb_plpython2u.control b/contrib/jsonb_plpython/jsonb_plpython2u.control
deleted file mode 100644
index d26368316b6..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# jsonb_plpython2u extension
-comment = 'transform between jsonb and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/jsonb_plpython2'
-relocatable = true
-requires = 'plpython2u'
diff --git a/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql b/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
deleted file mode 100644
index 3fa89885a63..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION jsonb_plpythonu" to load this file. \quit
-
-CREATE FUNCTION jsonb_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_to_jsonb(val internal) RETURNS jsonb
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR jsonb LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION jsonb_to_plpython(internal),
-    TO SQL WITH FUNCTION plpython_to_jsonb(internal)
-);
-
-COMMENT ON TRANSFORM FOR jsonb LANGUAGE plpythonu IS 'transform between jsonb and Python';
diff --git a/contrib/jsonb_plpython/jsonb_plpythonu.control b/contrib/jsonb_plpython/jsonb_plpythonu.control
deleted file mode 100644
index 6f8fa4f184b..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# jsonb_plpythonu extension
-comment = 'transform between jsonb and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/jsonb_plpython2'
-relocatable = true
-requires = 'plpythonu'
diff --git a/contrib/jsonb_plpython/sql/jsonb_plpython.sql b/contrib/jsonb_plpython/sql/jsonb_plpython.sql
index 2ee1bca0a98..29dc33279a0 100644
--- a/contrib/jsonb_plpython/sql/jsonb_plpython.sql
+++ b/contrib/jsonb_plpython/sql/jsonb_plpython.sql
@@ -1,8 +1,8 @@
-CREATE EXTENSION jsonb_plpython2u CASCADE;
+CREATE EXTENSION jsonb_plpython3u CASCADE;
 
 -- test jsonb -> python dict
 CREATE FUNCTION test1(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -15,7 +15,7 @@ SELECT test1('{"a": 1, "c": "NULL"}'::jsonb);
 -- test jsonb -> python dict
 -- complex dict with dicts as value
 CREATE FUNCTION test1complex(val jsonb) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -29,7 +29,7 @@ SELECT test1complex('{"d": {"d": 1}}'::jsonb);
 -- test jsonb[] -> python dict
 -- dict with array as value
 CREATE FUNCTION test1arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -42,7 +42,7 @@ SELECT test1arr('{"d":[12, 1]}'::jsonb);
 -- test jsonb[] -> python list
 -- simple list
 CREATE FUNCTION test2arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -55,7 +55,7 @@ SELECT test2arr('[12, 1]'::jsonb);
 -- test jsonb[] -> python list
 -- array of dicts
 CREATE FUNCTION test3arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -67,7 +67,7 @@ SELECT test3arr('[{"a": 1, "b": 2}, {"c": 3,"d": 4}]'::jsonb);
 
 -- test jsonb int -> python int
 CREATE FUNCTION test1int(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == 1)
@@ -78,7 +78,7 @@ SELECT test1int('1'::jsonb);
 
 -- test jsonb string -> python string
 CREATE FUNCTION test1string(val jsonb) RETURNS text
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == "a")
@@ -89,7 +89,7 @@ SELECT test1string('"a"'::jsonb);
 
 -- test jsonb null -> python None
 CREATE FUNCTION test1null(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == None)
@@ -100,7 +100,7 @@ SELECT test1null('null'::jsonb);
 
 -- test python -> jsonb
 CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 as $$
 return val
@@ -129,7 +129,7 @@ SELECT roundtrip('["string", "string2"]'::jsonb);
 
 -- complex numbers -> jsonb
 CREATE FUNCTION testComplexNumbers() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 1 + 2j
@@ -140,7 +140,7 @@ SELECT testComplexNumbers();
 
 -- range -> jsonb
 CREATE FUNCTION testRange() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = range(3)
@@ -151,7 +151,7 @@ SELECT testRange();
 
 -- 0xff -> jsonb
 CREATE FUNCTION testDecimal() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 0xff
@@ -162,7 +162,7 @@ SELECT testDecimal();
 
 -- tuple -> jsonb
 CREATE FUNCTION testTuple() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = (1, 'String', None)
@@ -173,7 +173,7 @@ SELECT testTuple();
 
 -- interesting dict -> jsonb
 CREATE FUNCTION test_dict1() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = {"a": 1, None: 2, 33: 3}
diff --git a/contrib/ltree_plpython/.gitignore b/contrib/ltree_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/ltree_plpython/.gitignore
+++ b/contrib/ltree_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile
index 0bccb111e6b..406d2789c9c 100644
--- a/contrib/ltree_plpython/Makefile
+++ b/contrib/ltree_plpython/Makefile
@@ -10,7 +10,6 @@ EXTENSION = ltree_plpython3u
 DATA = ltree_plpython3u--1.0.sql
 
 REGRESS = ltree_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 PG_CPPFLAGS = $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
@@ -37,9 +36,4 @@ SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
 
 REGRESS_OPTS += --load-extension=ltree
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=ltree_plpythonu
-endif
 EXTRA_INSTALL += contrib/ltree
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/ltree_plpython/expected/ltree_plpython.out b/contrib/ltree_plpython/expected/ltree_plpython.out
index f28897fee48..bd32541fdb3 100644
--- a/contrib/ltree_plpython/expected/ltree_plpython.out
+++ b/contrib/ltree_plpython/expected/ltree_plpython.out
@@ -1,7 +1,7 @@
-CREATE EXTENSION ltree_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION ltree_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 CREATE FUNCTION test1(val ltree) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -15,7 +15,7 @@ INFO:  ['aa', 'bb', 'cc']
 (1 row)
 
 CREATE FUNCTION test1n(val ltree) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -29,7 +29,7 @@ INFO:  ['aa', 'bb', 'cc']
 (1 row)
 
 CREATE FUNCTION test2() RETURNS ltree
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 return ['foo', 'bar', 'baz']
diff --git a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql b/contrib/ltree_plpython/ltree_plpython2u--1.0.sql
deleted file mode 100644
index 5c4a7037013..00000000000
--- a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/* contrib/ltree_plpython/ltree_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION ltree_plpython2u" to load this file. \quit
-
-CREATE FUNCTION ltree_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'ltree_to_plpython';
-
-CREATE TRANSFORM FOR ltree LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION ltree_to_plpython2(internal)
-);
diff --git a/contrib/ltree_plpython/ltree_plpython2u.control b/contrib/ltree_plpython/ltree_plpython2u.control
deleted file mode 100644
index bedfd0acbad..00000000000
--- a/contrib/ltree_plpython/ltree_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# ltree_plpython2u extension
-comment = 'transform between ltree and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/ltree_plpython2'
-relocatable = true
-requires = 'ltree,plpython2u'
diff --git a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql b/contrib/ltree_plpython/ltree_plpythonu--1.0.sql
deleted file mode 100644
index ee93edf28b9..00000000000
--- a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/* contrib/ltree_plpython/ltree_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION ltree_plpythonu" to load this file. \quit
-
-CREATE FUNCTION ltree_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR ltree LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION ltree_to_plpython(internal)
-);
diff --git a/contrib/ltree_plpython/ltree_plpythonu.control b/contrib/ltree_plpython/ltree_plpythonu.control
deleted file mode 100644
index b03c89a2e6e..00000000000
--- a/contrib/ltree_plpython/ltree_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# ltree_plpythonu extension
-comment = 'transform between ltree and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/ltree_plpython2'
-relocatable = true
-requires = 'ltree,plpythonu'
diff --git a/contrib/ltree_plpython/sql/ltree_plpython.sql b/contrib/ltree_plpython/sql/ltree_plpython.sql
index 210f5428a5a..0b8d28399a6 100644
--- a/contrib/ltree_plpython/sql/ltree_plpython.sql
+++ b/contrib/ltree_plpython/sql/ltree_plpython.sql
@@ -1,8 +1,8 @@
-CREATE EXTENSION ltree_plpython2u CASCADE;
+CREATE EXTENSION ltree_plpython3u CASCADE;
 
 
 CREATE FUNCTION test1(val ltree) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -13,7 +13,7 @@ SELECT test1('aa.bb.cc'::ltree);
 
 
 CREATE FUNCTION test1n(val ltree) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -24,7 +24,7 @@ SELECT test1n('aa.bb.cc'::ltree);
 
 
 CREATE FUNCTION test2() RETURNS ltree
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 return ['foo', 'bar', 'baz']
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index a9946262398..878a6397fa8 100644
--- a/src/tools/msvc/vcregress.pl
+++ b/src/tools/msvc/vcregress.pl
@@ -313,55 +313,6 @@ sub taptest
 	return;
 }
 
-sub mangle_plpython3
-{
-	my $tests = shift;
-	mkdir "results" unless -d "results";
-	mkdir "sql/python3";
-	mkdir "results/python3";
-	mkdir "expected/python3";
-
-	foreach my $test (@$tests)
-	{
-		local $/ = undef;
-		foreach my $dir ('sql', 'expected')
-		{
-			my $extension = ($dir eq 'sql' ? 'sql' : 'out');
-
-			my @files =
-			  glob("$dir/$test.$extension $dir/${test}_[0-9].$extension");
-			foreach my $file (@files)
-			{
-				open(my $handle, '<', $file)
-				  || die "test file $file not found";
-				my $contents = <$handle>;
-				close($handle);
-				do
-				{
-					s/<type 'exceptions\.([[:alpha:]]*)'>/<class '$1'>/g;
-					s/<type 'long'>/<class 'int'>/g;
-					s/([0-9][0-9]*)L/$1/g;
-					s/([ [{])u"/$1"/g;
-					s/([ [{])u'/$1'/g;
-					s/def next/def __next__/g;
-					s/LANGUAGE plpython2?u/LANGUAGE plpython3u/g;
-					s/EXTENSION (\S*?)plpython2?u/EXTENSION $1plpython3u/g;
-					s/installing required extension "plpython2u"/installing required extension "plpython3u"/g;
-				  }
-				  for ($contents);
-				my $base = basename $file;
-				open($handle, '>', "$dir/python3/$base")
-				  || die "opening python 3 file for $file";
-				print $handle $contents;
-				close($handle);
-			}
-		}
-	}
-	do { s!^!python3/!; }
-	  foreach (@$tests);
-	return @$tests;
-}
-
 sub plcheck
 {
 	chdir "$topdir/src/pl";
@@ -385,8 +336,7 @@ sub plcheck
 		if ($lang eq 'plpython')
 		{
 			next
-			  unless -d "$topdir/$Config/plpython2"
-			  || -d "$topdir/$Config/plpython3";
+			  unless -d "$topdir/$Config/plpython3";
 			$lang = 'plpythonu';
 		}
 		else
@@ -396,8 +346,6 @@ sub plcheck
 		my @lang_args = ("--load-extension=$lang");
 		chdir $dir;
 		my @tests = fetchTests();
-		@tests = mangle_plpython3(\@tests)
-		  if $lang eq 'plpythonu' && -d "$topdir/$Config/plpython3";
 		if ($lang eq 'plperl')
 		{
 
@@ -461,28 +409,6 @@ sub subdircheck
 
 	my @opts = fetchRegressOpts();
 
-	# Special processing for python transform modules, see their respective
-	# Makefiles for more details regarding Python-version specific
-	# dependencies.
-	if ($module =~ /_plpython$/)
-	{
-		die "Python not enabled in configuration"
-		  if !defined($config->{python});
-
-		@opts = grep { $_ !~ /plpythonu/ } @opts;
-
-		if (-d "$topdir/$Config/plpython2")
-		{
-			push @opts, "--load-extension=plpythonu";
-			push @opts, '--load-extension=' . $module . 'u';
-		}
-		else
-		{
-			# must be python 3
-			@tests = mangle_plpython3(\@tests);
-		}
-	}
-
 	print "============================================================\n";
 	print "Checking $module\n";
 	my @args = (
-- 
2.34.0

v5-0004-WIP-plpython-Code-cleanup-related-to-removal-of-P.patchtext/x-diff; charset=us-asciiDownload
From 322a83843479d4c224724ddac4a75dfc307510c2 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 15 Feb 2022 10:41:40 -0800
Subject: [PATCH v5 4/4] WIP: plpython: Code cleanup related to removal of
 Python 2 support.

---
 src/pl/plpython/plpy_cursorobject.c |  2 +-
 src/pl/plpython/plpy_main.c         | 55 ++++-------------------------
 src/pl/plpython/plpy_plpymodule.c   | 16 ---------
 src/pl/plpython/plpy_plpymodule.h   |  2 --
 src/pl/plpython/plpy_resultobject.c |  8 -----
 src/pl/plpython/plpy_typeio.c       |  8 -----
 src/pl/plpython/plpy_util.c         |  3 --
 src/pl/plpython/plpy_util.h         |  2 --
 src/pl/plpython/plpython.h          | 13 +++----
 9 files changed, 11 insertions(+), 98 deletions(-)

diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index 08d8b607e38..f8591358bc8 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -40,7 +40,7 @@ static PyTypeObject PLy_CursorType = {
 	.tp_name = "PLyCursor",
 	.tp_basicsize = sizeof(PLyCursorObject),
 	.tp_dealloc = PLy_cursor_dealloc,
-	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER,
+	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
 	.tp_doc = PLy_cursor_doc,
 	.tp_iter = PyObject_SelfIter,
 	.tp_iternext = PLy_cursor_iternext,
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index 3eedaa80da7..21303c1586b 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -28,27 +28,13 @@
  * exported functions
  */
 
-#if PY_MAJOR_VERSION >= 3
-/* Use separate names to reduce confusion */
-#define plpython_validator plpython3_validator
-#define plpython_call_handler plpython3_call_handler
-#define plpython_inline_handler plpython3_inline_handler
-#endif
-
 extern void _PG_init(void);
 
 PG_MODULE_MAGIC;
 
-PG_FUNCTION_INFO_V1(plpython_validator);
-PG_FUNCTION_INFO_V1(plpython_call_handler);
-PG_FUNCTION_INFO_V1(plpython_inline_handler);
-
-#if PY_MAJOR_VERSION < 3
-/* Define aliases plpython2_call_handler etc */
-PG_FUNCTION_INFO_V1(plpython2_validator);
-PG_FUNCTION_INFO_V1(plpython2_call_handler);
-PG_FUNCTION_INFO_V1(plpython2_inline_handler);
-#endif
+PG_FUNCTION_INFO_V1(plpython3_validator);
+PG_FUNCTION_INFO_V1(plpython3_call_handler);
+PG_FUNCTION_INFO_V1(plpython3_inline_handler);
 
 
 static bool PLy_procedure_is_trigger(Form_pg_proc procStruct);
@@ -125,13 +111,9 @@ PLy_initialize(void)
 	if (inited)
 		return;
 
-#if PY_MAJOR_VERSION >= 3
 	PyImport_AppendInittab("plpy", PyInit_plpy);
-#endif
 	Py_Initialize();
-#if PY_MAJOR_VERSION >= 3
 	PyImport_ImportModule("plpy");
-#endif
 	PLy_init_interp();
 	PLy_init_plpy();
 	if (PyErr_Occurred())
@@ -171,7 +153,7 @@ PLy_init_interp(void)
 }
 
 Datum
-plpython_validator(PG_FUNCTION_ARGS)
+plpython3_validator(PG_FUNCTION_ARGS)
 {
 	Oid			funcoid = PG_GETARG_OID(0);
 	HeapTuple	tuple;
@@ -203,17 +185,8 @@ plpython_validator(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-#if PY_MAJOR_VERSION < 3
 Datum
-plpython2_validator(PG_FUNCTION_ARGS)
-{
-	/* call plpython validator with our fcinfo so it gets our oid */
-	return plpython_validator(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
-Datum
-plpython_call_handler(PG_FUNCTION_ARGS)
+plpython3_call_handler(PG_FUNCTION_ARGS)
 {
 	bool		nonatomic;
 	Datum		retval;
@@ -284,16 +257,8 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 	return retval;
 }
 
-#if PY_MAJOR_VERSION < 3
 Datum
-plpython2_call_handler(PG_FUNCTION_ARGS)
-{
-	return plpython_call_handler(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
-Datum
-plpython_inline_handler(PG_FUNCTION_ARGS)
+plpython3_inline_handler(PG_FUNCTION_ARGS)
 {
 	LOCAL_FCINFO(fake_fcinfo, 0);
 	InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
@@ -368,14 +333,6 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-#if PY_MAJOR_VERSION < 3
-Datum
-plpython2_inline_handler(PG_FUNCTION_ARGS)
-{
-	return plpython_inline_handler(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
 static bool
 PLy_procedure_is_trigger(Form_pg_proc procStruct)
 {
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
index 0365acc95b0..0eefd34afcd 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -109,7 +109,6 @@ static PyMethodDef PLy_exc_methods[] = {
 	{NULL, NULL, 0, NULL}
 };
 
-#if PY_MAJOR_VERSION >= 3
 static PyModuleDef PLy_module = {
 	PyModuleDef_HEAD_INIT,
 	.m_name = "plpy",
@@ -141,7 +140,6 @@ PyInit_plpy(void)
 
 	return m;
 }
-#endif							/* PY_MAJOR_VERSION >= 3 */
 
 void
 PLy_init_plpy(void)
@@ -150,10 +148,6 @@ PLy_init_plpy(void)
 			   *main_dict,
 			   *plpy_mod;
 
-#if PY_MAJOR_VERSION < 3
-	PyObject   *plpy;
-#endif
-
 	/*
 	 * initialize plpy module
 	 */
@@ -162,13 +156,7 @@ PLy_init_plpy(void)
 	PLy_subtransaction_init_type();
 	PLy_cursor_init_type();
 
-#if PY_MAJOR_VERSION >= 3
 	PyModule_Create(&PLy_module);
-	/* for Python 3 we initialized the exceptions in PyInit_plpy */
-#else
-	plpy = Py_InitModule("plpy", PLy_methods);
-	PLy_add_exceptions(plpy);
-#endif
 
 	/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
 
@@ -191,11 +179,7 @@ PLy_add_exceptions(PyObject *plpy)
 	PyObject   *excmod;
 	HASHCTL		hash_ctl;
 
-#if PY_MAJOR_VERSION < 3
-	excmod = Py_InitModule("spiexceptions", PLy_exc_methods);
-#else
 	excmod = PyModule_Create(&PLy_exc_module);
-#endif
 	if (excmod == NULL)
 		PLy_elog(ERROR, "could not create the spiexceptions module");
 
diff --git a/src/pl/plpython/plpy_plpymodule.h b/src/pl/plpython/plpy_plpymodule.h
index 54d78101ceb..ad6436aca78 100644
--- a/src/pl/plpython/plpy_plpymodule.h
+++ b/src/pl/plpython/plpy_plpymodule.h
@@ -11,9 +11,7 @@
 extern HTAB *PLy_spi_exceptions;
 
 
-#if PY_MAJOR_VERSION >= 3
 PyMODINIT_FUNC PyInit_plpy(void);
-#endif
 extern void PLy_init_plpy(void);
 
 #endif							/* PLPY_PLPYMODULE_H */
diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c
index 54f39419c84..f289617ba80 100644
--- a/src/pl/plpython/plpy_resultobject.c
+++ b/src/pl/plpython/plpy_resultobject.c
@@ -226,19 +226,11 @@ PLy_result_str(PyObject *arg)
 {
 	PLyResultObject *ob = (PLyResultObject *) arg;
 
-#if PY_MAJOR_VERSION >= 3
 	return PyUnicode_FromFormat("<%s status=%S nrows=%S rows=%S>",
 								Py_TYPE(ob)->tp_name,
 								ob->status,
 								ob->nrows,
 								ob->rows);
-#else
-	return PyString_FromFormat("<%s status=%ld nrows=%ld rows=%s>",
-							   ob->ob_type->tp_name,
-							   PyInt_AsLong(ob->status),
-							   PyInt_AsLong(ob->nrows),
-							   PyString_AsString(PyObject_Str(ob->rows)));
-#endif
 }
 
 static PyObject *
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 5e807b139f1..adf37a9b882 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -1032,25 +1032,17 @@ PLyObject_AsString(PyObject *plrv)
 	else if (PyFloat_Check(plrv))
 	{
 		/* use repr() for floats, str() is lossy */
-#if PY_MAJOR_VERSION >= 3
 		PyObject   *s = PyObject_Repr(plrv);
 
 		plrv_bo = PLyUnicode_Bytes(s);
 		Py_XDECREF(s);
-#else
-		plrv_bo = PyObject_Repr(plrv);
-#endif
 	}
 	else
 	{
-#if PY_MAJOR_VERSION >= 3
 		PyObject   *s = PyObject_Str(plrv);
 
 		plrv_bo = PLyUnicode_Bytes(s);
 		Py_XDECREF(s);
-#else
-		plrv_bo = PyObject_Str(plrv);
-#endif
 	}
 	if (!plrv_bo)
 		PLy_elog(ERROR, "could not create string representation of Python object");
diff --git a/src/pl/plpython/plpy_util.c b/src/pl/plpython/plpy_util.c
index 4a7d7264d79..693d0396c48 100644
--- a/src/pl/plpython/plpy_util.c
+++ b/src/pl/plpython/plpy_util.c
@@ -95,7 +95,6 @@ PLyUnicode_AsString(PyObject *unicode)
 	return rv;
 }
 
-#if PY_MAJOR_VERSION >= 3
 /*
  * Convert a C string in the PostgreSQL server encoding to a Python
  * unicode object.  Reference ownership is passed to the caller.
@@ -126,5 +125,3 @@ PLyUnicode_FromString(const char *s)
 {
 	return PLyUnicode_FromStringAndSize(s, strlen(s));
 }
-
-#endif							/* PY_MAJOR_VERSION >= 3 */
diff --git a/src/pl/plpython/plpy_util.h b/src/pl/plpython/plpy_util.h
index c9ba7edc0ec..7c6577925ea 100644
--- a/src/pl/plpython/plpy_util.h
+++ b/src/pl/plpython/plpy_util.h
@@ -11,9 +11,7 @@
 extern PyObject *PLyUnicode_Bytes(PyObject *unicode);
 extern char *PLyUnicode_AsString(PyObject *unicode);
 
-#if PY_MAJOR_VERSION >= 3
 extern PyObject *PLyUnicode_FromString(const char *s);
 extern PyObject *PLyUnicode_FromStringAndSize(const char *s, Py_ssize_t size);
-#endif
 
 #endif							/* PLPY_UTIL_H */
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 05e4362dab9..798c3707301 100644
--- a/src/pl/plpython/plpython.h
+++ b/src/pl/plpython/plpython.h
@@ -69,26 +69,21 @@
  * compatibility layer for Python 3 that when asked to convert a C
  * string to a Python string it converts the C string from the
  * PostgreSQL server encoding to a Python Unicode object.
+ *
+ * FIXME
  */
-#if PY_MAJOR_VERSION >= 3
 #define PyString_Check(x) 0
 #define PyString_AsString(x) PLyUnicode_AsString(x)
 #define PyString_FromString(x) PLyUnicode_FromString(x)
 #define PyString_FromStringAndSize(x, size) PLyUnicode_FromStringAndSize(x, size)
-#endif
 
 /*
  * Python 3 only has long.
+ *
+ * FIXME
  */
-#if PY_MAJOR_VERSION >= 3
 #define PyInt_FromLong(x) PyLong_FromLong(x)
 #define PyInt_AsLong(x) PyLong_AsLong(x)
-#endif
-
-/* Python 3 removed the Py_TPFLAGS_HAVE_ITER flag */
-#if PY_MAJOR_VERSION >= 3
-#define Py_TPFLAGS_HAVE_ITER 0
-#endif
 
 /* define our text domain for translations */
 #undef TEXTDOMAIN
-- 
2.34.0

#42Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Andres Freund (#41)
Re: Time to drop plpython2?

On 15.02.22 22:40, Andres Freund wrote:

1: plpython: Reject Python 2 during build configuration.

There is a bit of redundancy in the new wording in installation.sgml:

"The minimum required version is <productname>Python</productname> 3.2
or later."

There is also a small bit in install-windows.sgml that would be worth
updating:

<programlisting>
$config->{python} = 'c:\python26';
</programlisting>

Other than that, this looks fine to me, and we should move forward with
this now.

2: plpython: Remove plpythonu, plpython2u extensions.
3: plpython: Remove regression test infrastructure for Python 2.

The removal of the Python 2 contrib extension variants ended up in patch
3, which seems incorrect.

#43Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#42)
Re: Time to drop plpython2?

Hi,

On 2022-02-16 15:43:19 +0100, Peter Eisentraut wrote:

On 15.02.22 22:40, Andres Freund wrote:

1: plpython: Reject Python 2 during build configuration.

There is a bit of redundancy in the new wording in installation.sgml:

"The minimum required version is <productname>Python</productname> 3.2 or
later."

I stared for a bit, and I just don't see the redundancy?

There is also a small bit in install-windows.sgml that would be worth
updating:

<programlisting>
$config->{python} = 'c:\python26';
</programlisting>

Yea. I saw that but ended up with not updating it, because it already was out
of date ;). But I guess I'll just put 310 there or something.

Other than that, this looks fine to me, and we should move forward with this
now.

Cool, will apply 1) later today.

2: plpython: Remove plpythonu, plpython2u extensions.
3: plpython: Remove regression test infrastructure for Python 2.

The removal of the Python 2 contrib extension variants ended up in patch 3,
which seems incorrect.

Uh, huh. That was a mistake, not sure what happened...

Greetings,

Andres Freund

#44Chapman Flack
chap@anastigmatix.net
In reply to: Andres Freund (#43)
Re: Time to drop plpython2?

On 02/16/22 14:58, Andres Freund wrote:

"The minimum required version is <productname>Python</productname> 3.2 or
later."

I stared for a bit, and I just don't see the redundancy?

"minimum ... or later" maybe?

Regards,
-Chap

#45Andres Freund
andres@anarazel.de
In reply to: Chapman Flack (#44)
Re: Time to drop plpython2?

On 2022-02-16 15:05:36 -0500, Chapman Flack wrote:

On 02/16/22 14:58, Andres Freund wrote:

"The minimum required version is <productname>Python</productname> 3.2 or
later."

I stared for a bit, and I just don't see the redundancy?

"minimum ... or later" maybe?

Ah. Thanks.

#46Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#43)
Re: Time to drop plpython2?

On 2022-02-16 11:58:50 -0800, Andres Freund wrote:

Cool, will apply 1) later today.

Done. Curious how red the BF will turn out to be. Let's hope it's not
too bad.

#47Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#46)
Re: Time to drop plpython2?

Hi,

On 2022-02-16 22:52:08 -0800, Andres Freund wrote:

On 2022-02-16 11:58:50 -0800, Andres Freund wrote:

Cool, will apply 1) later today.

Done. Curious how red the BF will turn out to be. Let's hope it's not
too bad.

I've pinged the owners of the animals failing so far:
- snakefly, massasauga
- jay, trilobite, hippopotamus, avocet
- myna, butterflyfish
- rhinoceros

Greetings,

Andres Freund

#48Michael Paquier
michael@paquier.xyz
In reply to: Andres Freund (#47)
Re: Time to drop plpython2?

On Wed, Feb 16, 2022 at 11:14:46PM -0800, Andres Freund wrote:

I've pinged the owners of the animals failing so far:
- myna, butterflyfish

These two are managed by a colleague, and I have an access to them.
They should get back to green quickly now, if I did not mess up..
--
Michael

#49Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#47)
Re: Time to drop plpython2?

Hi,

On 2022-02-16 23:14:46 -0800, Andres Freund wrote:

Done. Curious how red the BF will turn out to be. Let's hope it's not
too bad.

I've pinged the owners of the animals failing so far:

Now also pinged:
- curculio
- guaibasaurus
- snapper
- gadwall, takin

- snakefly, massasauga

Fixed.

- jay, trilobite, hippopotamus, avocet

Nothing yet.

- myna, butterflyfish

Fixed, as noted by Micheal on this thread.

- rhinoceros

Joe replied that he is afk, looking into it tomorrow.

Greetings,

Andres Freund

#50Mikael Kjellström
mikael.kjellstrom@mksoft.nu
In reply to: Andres Freund (#49)
Re: Time to drop plpython2?

On 2022-02-17 19:08, Andres Freund wrote:

I've pinged the owners of the animals failing so far:

Now also pinged:
- curculio

Should be fixed by now.

I did install the python3-package but the binary was called:

/usr/local/bin/python3.5

for some reason so configure didn't pick it up.

Fixed it by creating a symlink to:

/usr/local/bin/python3

/Mikael

#51Joe Conway
mail@joeconway.com
In reply to: Andres Freund (#49)
Re: Time to drop plpython2?

On 2/17/22 13:08, Andres Freund wrote:

On 2022-02-16 23:14:46 -0800, Andres Freund wrote:

Done. Curious how red the BF will turn out to be. Let's hope it's not
too bad.

- rhinoceros

Joe replied that he is afk, looking into it tomorrow.

I installed python3 packages (initially forgetting the devel package --
d'oh!) and changed build-farm.conf thusly:

8<-------------------
***************
*** 185,190 ****
--- 185,193 ----

build_env => {

+ # specify python 3
+ PYTHON => 'python3',
+
# use a dedicated cache for the build farm. this should
give us
# very high hit rates and slightly faster cache searching.
#
8<-------------------

That seems to have worked.

But the question is, is that the correct/recommended method?

Joe
--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#52Andres Freund
andres@anarazel.de
In reply to: Joe Conway (#51)
Re: Time to drop plpython2?

Hi,

On 2022-02-18 14:19:49 -0500, Joe Conway wrote:

On 2/17/22 13:08, Andres Freund wrote:

On 2022-02-16 23:14:46 -0800, Andres Freund wrote:

Done. Curious how red the BF will turn out to be. Let's hope it's not
too bad.

- rhinoceros

Joe replied that he is afk, looking into it tomorrow.

I installed python3 packages (initially forgetting the devel package --
d'oh!) and changed build-farm.conf thusly:

8<-------------------
***************
*** 185,190 ****
--- 185,193 ----

build_env => {

+ # specify python 3
+ PYTHON => 'python3',
+
# use a dedicated cache for the build farm. this should give
us
# very high hit rates and slightly faster cache searching.
#
8<-------------------

That seems to have worked.

But the question is, is that the correct/recommended method?

If python3 is in PATH, then you shouldn't need that part.

Greetings,

Andres Freund

#53Joe Conway
mail@joeconway.com
In reply to: Andres Freund (#52)
Re: Time to drop plpython2?

On 2/18/22 14:37, Andres Freund wrote:

Hi,

On 2022-02-18 14:19:49 -0500, Joe Conway wrote:

On 2/17/22 13:08, Andres Freund wrote:

On 2022-02-16 23:14:46 -0800, Andres Freund wrote:

Done. Curious how red the BF will turn out to be. Let's hope it's not
too bad.

- rhinoceros

Joe replied that he is afk, looking into it tomorrow.

I installed python3 packages (initially forgetting the devel package --
d'oh!) and changed build-farm.conf thusly:

8<-------------------
***************
*** 185,190 ****
--- 185,193 ----

build_env => {

+ # specify python 3
+ PYTHON => 'python3',
+
# use a dedicated cache for the build farm. this should give
us
# very high hit rates and slightly faster cache searching.
#
8<-------------------

That seems to have worked.

But the question is, is that the correct/recommended method?

If python3 is in PATH, then you shouldn't need that part.

Not quite -- python3 is definitely in the PATH:

8<-------------------
$ which python3
/usr/bin/python3
8<-------------------

And I gather that merely installing python3 RPMs on a RHEL-esque 7.X
system does not replace the python symlink:

8<-------------------
$ yum whatprovides /usr/bin/python

python-2.7.5-89.el7.x86_64 : An interpreted, interactive,
object-oriented programming language
Repo : base
Matched from:
Filename : /usr/bin/python

$ ll /usr/bin/python
lrwxrwxrwx. 1 root root 7 Mar 13 2021 /usr/bin/python -> python2
8<-------------------

Joe
--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#54Andres Freund
andres@anarazel.de
In reply to: Joe Conway (#53)
Re: Time to drop plpython2?

Hi,

On 2022-02-18 14:46:39 -0500, Joe Conway wrote:

On 2/18/22 14:37, Andres Freund wrote:

That seems to have worked.

But the question is, is that the correct/recommended method?

If python3 is in PATH, then you shouldn't need that part.

Not quite -- python3 is definitely in the PATH:

8<-------------------
$ which python3
/usr/bin/python3
8<-------------------

And I gather that merely installing python3 RPMs on a RHEL-esque 7.X system
does not replace the python symlink:

8<-------------------
$ yum whatprovides /usr/bin/python

python-2.7.5-89.el7.x86_64 : An interpreted, interactive, object-oriented
programming language
Repo : base
Matched from:
Filename : /usr/bin/python

$ ll /usr/bin/python
lrwxrwxrwx. 1 root root 7 Mar 13 2021 /usr/bin/python -> python2
8<-------------------

Yea, that all looks fine. What's the problem if you don't specify the
PYTHON=python3? We try python3, python in that order by default, so it should
pick up the same python3 you specify explicitly?

Or maybe I'm just misunderstanding what you're asking...

Greetings,

Andres Freund

#55Joe Conway
mail@joeconway.com
In reply to: Andres Freund (#54)
Re: Time to drop plpython2?

On 2/18/22 15:25, Andres Freund wrote:

On 2022-02-18 14:46:39 -0500, Joe Conway wrote:

$ ll /usr/bin/python
lrwxrwxrwx. 1 root root 7 Mar 13 2021 /usr/bin/python -> python2
8<-------------------

Yea, that all looks fine. What's the problem if you don't specify the
PYTHON=python3? We try python3, python in that order by default, so it should
pick up the same python3 you specify explicitly?

Initially I just installed the python3 RPMs and when I tried running
manually it was still error'ing on configure due to finding python2.

Even after adding EXPORT PYTHON=python3 to my ~/.bash_profile I was
seeing the same.

By adding PYTHON => 'python3' to build-farm.conf I saw that the error
changed to indicate missing python3-devel package. Once I installed
that, everything went green.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#56Andres Freund
andres@anarazel.de
In reply to: Joe Conway (#55)
Re: Time to drop plpython2?

Hi,

On 2022-02-18 15:35:37 -0500, Joe Conway wrote:

Initially I just installed the python3 RPMs and when I tried running
manually it was still error'ing on configure due to finding python2.

Even after adding EXPORT PYTHON=python3 to my ~/.bash_profile I was seeing
the same.

By adding PYTHON => 'python3' to build-farm.conf I saw that the error
changed to indicate missing python3-devel package. Once I installed that,
everything went green.

Hm. It definitely did test python3, earlier today:

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=rhinoceros&amp;dt=2022-02-18%2016%3A52%3A13

checking for python3... no
checking for python... /usr/bin/python

the next run then saw:

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=rhinoceros&amp;dt=2022-02-18%2017%3A50%3A09
checking for PYTHON... python3
configure: using python 3.6.8 (default, Nov 16 2020, 16:55:22)
checking for Python sysconfig module... yes
checking Python configuration directory... /usr/lib64/python3.6/config-3.6m-x86_64-linux-gnu
checking Python include directory... -I/usr/include/python3.6m

but then failed because the python headers weren't available:
checking for Python.h... no
configure: error: header file <Python.h> is required for Python

Note that this did *not* yet use PYTHON => 'python3' in build_env, but has it
in the environment starting the buildfarm client.

the next run succeeded, with 'PYTHON' => 'python3' in build env. But
presumably this just was because you installed the python3-devel package?

Greetings,

Andres Freund

#57Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Andres Freund (#49)
Re: Time to drop plpython2?

On 2022-Feb-17, Andres Freund wrote:

Now also pinged:
- guaibasaurus

Fixed now (apt install python3-dev), but I had initially added
PYTHON=>python3 to the .conf, unsuccessfully because I failed to install
the dev pkg. After the first success I removed that line. It should
still work if we do test python3 first, but if it does fail, then I'll
put that line back.

Thanks

--
Álvaro Herrera Valdivia, Chile — https://www.EnterpriseDB.com/
"The problem with the future is that it keeps turning into the present"
(Hobbes)

#58Joe Conway
mail@joeconway.com
In reply to: Andres Freund (#56)
Re: Time to drop plpython2?

On 2/18/22 15:53, Andres Freund wrote:

the next run succeeded, with 'PYTHON' => 'python3' in build env. But
presumably this just was because you installed the python3-devel package?

Ok, I guess I got confused when it failed due to the missing devel
package, because I removed the PYTHON => 'python3' from the build env
and it is still getting successfully past the configure stage. Sorry for
the noise.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#59Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#49)
Re: Time to drop plpython2?

Hi,

Thanks to some more buildfarm animal updates things are looking better. I
think there's now only three owners that haven't updated their animals
successfully. One of which I hadn't yet pinged (chipmunk / Heikki), done now.

There's snapper ("pgbf [ a t ] twiska.com"), and there's Mark Wong's large
menagerie. Mark said yesterday that he's working on updating.

There's one further failure, but the symptoms are quite different. I've also
pinged its owner. I think it's a problem on the system, rather than our side,
but less certain than with the other cases:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=haddock&amp;dt=2022-02-17%2023%3A36%3A09

checking Python.h usability... no
checking Python.h presence... yes
configure: WARNING: Python.h: present but cannot be compiled
configure: WARNING: Python.h: check for missing prerequisite headers?
configure: WARNING: Python.h: see the Autoconf documentation
configure: WARNING: Python.h: section "Present But Cannot Be Compiled"
configure: WARNING: Python.h: proceeding with the compiler's result
configure: WARNING: ## ---------------------------------------------- ##
configure: WARNING: ## Report this to pgsql-bugs@lists.postgresql.org ##
configure: WARNING: ## ---------------------------------------------- ##
checking for Python.h... no
configure: error: header file <Python.h> is required for Python

configure:19158: ccache gcc -c -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -m32 -I/usr/include/python3.9 -DWAIT_USE_POLL -D_POSIX_PTHREAD_SEMANTICS -I/usr/include/libxml2 conftest.c >&5
In file included from /usr/include/python3.9/Python.h:8,
from conftest.c:235:
/usr/include/python3.9/pyconfig.h:1443: warning: "SIZEOF_LONG" redefined
1443 | #define SIZEOF_LONG 8
|
conftest.c:183: note: this is the location of the previous definition
183 | #define SIZEOF_LONG 4
|
In file included from /usr/include/python3.9/Python.h:8,
from conftest.c:235:
/usr/include/python3.9/pyconfig.h:1467: warning: "SIZEOF_SIZE_T" redefined
1467 | #define SIZEOF_SIZE_T 8
|
conftest.c:182: note: this is the location of the previous definition
182 | #define SIZEOF_SIZE_T 4
|
In file included from /usr/include/python3.9/Python.h:8,
from conftest.c:235:
/usr/include/python3.9/pyconfig.h:1476: warning: "SIZEOF_VOID_P" redefined
1476 | #define SIZEOF_VOID_P 8
|
conftest.c:181: note: this is the location of the previous definition
181 | #define SIZEOF_VOID_P 4
|
In file included from /usr/include/python3.9/Python.h:63,
from conftest.c:235:
/usr/include/python3.9/pyport.h:736:2: error: #error "LONG_BIT definition appears wrong for platform (bad gcc/glibc config?)."
736 | #error "LONG_BIT definition appears wrong for platform (bad gcc/glibc config?)."
| ^~~~~

This is a 64bit host, targetting 32bit "CFLAGS' => '-m32'. However it linked
successfully against python 2.

Greetings,

Andres Freund

#60Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#59)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

There's one further failure, but the symptoms are quite different. I've also
pinged its owner. I think it's a problem on the system, rather than our side,
but less certain than with the other cases:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=haddock&amp;dt=2022-02-17%2023%3A36%3A09

This one was discussed on the buildfarm-owners list last month.
There's no 32-bit python3 on that box, and apparently no plans
to install one --- it sounded like the box is due for retirement
anyway.

regards, tom lane

#61Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#60)
Re: Time to drop plpython2?

Hi,

On 2022-02-18 18:09:19 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

There's one further failure, but the symptoms are quite different. I've also
pinged its owner. I think it's a problem on the system, rather than our side,
but less certain than with the other cases:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=haddock&amp;dt=2022-02-17%2023%3A36%3A09

This one was discussed on the buildfarm-owners list last month.
There's no 32-bit python3 on that box, and apparently no plans
to install one --- it sounded like the box is due for retirement
anyway.

Any chance that was a private response? I just looked in the buildfarm-members
list (I assume you meant that?), and didn't see anything:

/messages/by-id/3162195.1642093011@sss.pgh.pa.us

Greetings,

Andres Freund

#62Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#61)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

On 2022-02-18 18:09:19 -0500, Tom Lane wrote:

This one was discussed on the buildfarm-owners list last month.
There's no 32-bit python3 on that box, and apparently no plans
to install one --- it sounded like the box is due for retirement
anyway.

Any chance that was a private response? I just looked in the buildfarm-members
list (I assume you meant that?), and didn't see anything:
/messages/by-id/3162195.1642093011@sss.pgh.pa.us

Oh ... hm, yeah, looking at my mail logs, that came in as private mail and
there's no corresponding buildfarm-members traffic. I did not keep the
message unfortunately, but the owner indicated that he wasn't planning to
bother updating python. Which is fine, but maybe we should press him to
remove --with-python instead of disabling the box altogether --- we don't
have a lot of Solaris clones in the farm.

regards, tom lane

#63Mark Wong
markwkm@postgresql.org
In reply to: Andres Freund (#59)
Re: Time to drop plpython2?

Hi everyone,

On Fri, Feb 18, 2022 at 02:41:04PM -0800, Andres Freund wrote:

There's snapper ("pgbf [ a t ] twiska.com"), and there's Mark Wong's large
menagerie. Mark said yesterday that he's working on updating.

I've made one pass. Hopefully I didn't make any mistakes. :)

Regards,
Mark

#64Michael Paquier
michael@paquier.xyz
In reply to: Andres Freund (#49)
Re: Time to drop plpython2?

On Thu, Feb 17, 2022 at 10:08:55AM -0800, Andres Freund wrote:

On 2022-02-16 23:14:46 -0800, Andres Freund wrote:

I've pinged the owners of the animals failing so far:
- myna, butterflyfish

Fixed, as noted by Micheal on this thread.

Fixed is an incorrect word here, "temporarily bypassed" fits better :)

Unfortunately, I had to remove --with-python on both animals for the
time being, as I was not able to figure out why Python.h could not be
found in those installations, and it was Friday afternoon. I'll try
to investigate and re-enable that some time next week.
--
Michael

#65Andres Freund
andres@anarazel.de
In reply to: Mark Wong (#63)
Re: Time to drop plpython2?

Hi,

On 2022-02-19 02:00:28 +0000, Mark Wong wrote:

On Fri, Feb 18, 2022 at 02:41:04PM -0800, Andres Freund wrote:

There's snapper ("pgbf [ a t ] twiska.com"), and there's Mark Wong's large
menagerie. Mark said yesterday that he's working on updating.

I've made one pass. Hopefully I didn't make any mistakes. :)

Unfortunately it looks like it wasn't quite enough. All, or nearly all, your
animals that ran since still seem to be failing in the same spot...

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=gadwall&amp;dt=2022-02-19%2011%3A22%3A48

checking Python.h usability... no
checking Python.h presence... no
checking for Python.h... no
configure: error: header file <Python.h> is required for Python

For that machine (and the other debian based ones) the relevant package likely
is python3-dev.

For the Red Hat and Suse ones, it's likely python3-devel.

I've wondered before if it's worth maintaining a list of packages for
dependencies for at least the more popular distros. It's annoying to have to
figure it out everytime one needs to test something.

FWIW, here's the recipe I just used to verify the packages necessary for
Python.h to be found:

$ podman run --rm -it opensuse/leap
# zypper install -y python3
# ls -l $(python3 -c "import sysconfig; print(sysconfig.get_config_var('INCLUDEPY'))")/Python.h
<file not found>
# zypper install -y python3-devel
# ls -l $(python3 -c "import sysconfig; print(sysconfig.get_config_var('INCLUDEPY'))")/Python.h
-rw-r--r-- 1 root root 3221 Jan 4 14:04 /usr/include/python3.6m/Python.h

(Wow, zypper repos are expensive to refresh. And I thought dnf was slow doing
so, compared to apt.)

Greetings,

Andres Freund

#66Mark Wong
markwkm@postgresql.org
In reply to: Andres Freund (#65)
Re: Time to drop plpython2?

On Sat, Feb 19, 2022 at 08:22:29AM -0800, Andres Freund wrote:

Hi,

On 2022-02-19 02:00:28 +0000, Mark Wong wrote:

On Fri, Feb 18, 2022 at 02:41:04PM -0800, Andres Freund wrote:

There's snapper ("pgbf [ a t ] twiska.com"), and there's Mark Wong's large
menagerie. Mark said yesterday that he's working on updating.

I've made one pass. Hopefully I didn't make any mistakes. :)

Unfortunately it looks like it wasn't quite enough. All, or nearly all, your
animals that ran since still seem to be failing in the same spot...

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=gadwall&amp;dt=2022-02-19%2011%3A22%3A48

checking Python.h usability... no
checking Python.h presence... no
checking for Python.h... no
configure: error: header file <Python.h> is required for Python

For that machine (and the other debian based ones) the relevant package likely
is python3-dev.

For the Red Hat and Suse ones, it's likely python3-devel.

I've wondered before if it's worth maintaining a list of packages for
dependencies for at least the more popular distros. It's annoying to have to
figure it out everytime one needs to test something.

FWIW, here's the recipe I just used to verify the packages necessary for
Python.h to be found:

$ podman run --rm -it opensuse/leap
# zypper install -y python3
# ls -l $(python3 -c "import sysconfig; print(sysconfig.get_config_var('INCLUDEPY'))")/Python.h
<file not found>
# zypper install -y python3-devel
# ls -l $(python3 -c "import sysconfig; print(sysconfig.get_config_var('INCLUDEPY'))")/Python.h
-rw-r--r-- 1 root root 3221 Jan 4 14:04 /usr/include/python3.6m/Python.h

(Wow, zypper repos are expensive to refresh. And I thought dnf was slow doing
so, compared to apt.)

Oops, made another pass for python3 dev libraries.

I can't seem to find archived ppc repos OpenSUSE Leap 43.2. I'm
debating whether to disable python or upgrade/rebrand that animal for a
newer SUSE release. I've stopped my cron jobs on this animal for the
time being.

Regards,
Mark

#67Andres Freund
andres@anarazel.de
In reply to: Mark Wong (#66)
Re: Time to drop plpython2?

Hi,

On 2022-02-21 09:49:32 -0800, Mark Wong wrote:

Oops, made another pass for python3 dev libraries.

Thanks!

I can't seem to find archived ppc repos OpenSUSE Leap 43.2. I'm
debating whether to disable python or upgrade/rebrand that animal for a
newer SUSE release. I've stopped my cron jobs on this animal for the
time being.

I assume you mean leap 42.3, that seemed to be the newest 4x.x? If so that's
been archived 2019-07-01 [1]https://lists.opensuse.org/archives/list/security-announce@lists.opensuse.org/message/6CIQPV3H6J4AIIXUKUGI4IESMTHIFFFB/.

Leap's versioning is, uh, confusing. 13 -> 42 -> 15. Yea.

I don't think it's really useful to run out-of-support distribution
versions. Leaving security etc aside, not being able to install packages seems
sufficient reason to upgrade.

Greetings,

Andres Freund

[1]: https://lists.opensuse.org/archives/list/security-announce@lists.opensuse.org/message/6CIQPV3H6J4AIIXUKUGI4IESMTHIFFFB/

#68Tom Lane
tgl@sss.pgh.pa.us
In reply to: Mark Wong (#66)
Re: Time to drop plpython2?

Mark Wong <markwkm@postgresql.org> writes:

On Sat, Feb 19, 2022 at 08:22:29AM -0800, Andres Freund wrote:

Unfortunately it looks like it wasn't quite enough. All, or nearly all, your
animals that ran since still seem to be failing in the same spot...

Oops, made another pass for python3 dev libraries.

You might need to do one more thing, which is manually blow away the cache
files under $BUILDFARM/accache. For example, on demoiselle everything
looks fine in HEAD, but the back branches are failing like this:

checking for python... (cached) /usr/bin/python
./configure: line 10334: /usr/bin/python: No such file or directory
configure: using python
./configure: line 10342: test: : integer expression expected
checking for Python sysconfig module... no
configure: error: sysconfig module not found

Very recent versions of the buildfarm script will discard accache
automatically after a configure or make failure, but I think the
REL_11 you're running here doesn't have that defense. It'll only
flush accache after a change in the configure script in git.

regards, tom lane

#69Mark Wong
markwkm@postgresql.org
In reply to: Tom Lane (#68)
Re: Time to drop plpython2?

On Mon, Feb 21, 2022 at 03:28:35PM -0500, Tom Lane wrote:

Mark Wong <markwkm@postgresql.org> writes:

On Sat, Feb 19, 2022 at 08:22:29AM -0800, Andres Freund wrote:

Unfortunately it looks like it wasn't quite enough. All, or nearly all, your
animals that ran since still seem to be failing in the same spot...

Oops, made another pass for python3 dev libraries.

You might need to do one more thing, which is manually blow away the cache
files under $BUILDFARM/accache. For example, on demoiselle everything
looks fine in HEAD, but the back branches are failing like this:

checking for python... (cached) /usr/bin/python
./configure: line 10334: /usr/bin/python: No such file or directory
configure: using python
./configure: line 10342: test: : integer expression expected
checking for Python sysconfig module... no
configure: error: sysconfig module not found

Very recent versions of the buildfarm script will discard accache
automatically after a configure or make failure, but I think the
REL_11 you're running here doesn't have that defense. It'll only
flush accache after a change in the configure script in git.

Take 3. :)

I've upgraded everyone to the v14 buildfarm scripts and made sure the
--test passed on HEAD on each one. So I hopefully didn't miss any
(other than the one EOL OpenSUSE version that I will plan on upgrading.)

Regards,
Mark

#70Tom Lane
tgl@sss.pgh.pa.us
In reply to: Mark Wong (#69)
Re: Time to drop plpython2?

Mark Wong <markwkm@postgresql.org> writes:

Take 3. :)

I've upgraded everyone to the v14 buildfarm scripts and made sure the
--test passed on HEAD on each one. So I hopefully didn't miss any
(other than the one EOL OpenSUSE version that I will plan on upgrading.)

Thanks!

However ... it seems like most of your animals haven't run at all
in the last couple of days. Did you maybe forget to re-enable
their cron jobs after messing with them, or something like that?

regards, tom lane

#71Mark Wong
markwkm@postgresql.org
In reply to: Tom Lane (#70)
Re: Time to drop plpython2?

On Tue, Feb 22, 2022 at 08:50:07PM -0500, Tom Lane wrote:

Mark Wong <markwkm@postgresql.org> writes:

Take 3. :)

I've upgraded everyone to the v14 buildfarm scripts and made sure the
--test passed on HEAD on each one. So I hopefully didn't miss any
(other than the one EOL OpenSUSE version that I will plan on upgrading.)

Thanks!

However ... it seems like most of your animals haven't run at all
in the last couple of days. Did you maybe forget to re-enable
their cron jobs after messing with them, or something like that?

Uh oh, more user error. I tested run_build but run_branches wasn't
happy. I'll start working through that...

Regards,
Mark

#72Mark Wong
markwkm@postgresql.org
In reply to: Mark Wong (#71)
Re: Time to drop plpython2?

On Wed, Feb 23, 2022 at 07:59:01AM -0800, Mark Wong wrote:

On Tue, Feb 22, 2022 at 08:50:07PM -0500, Tom Lane wrote:

Mark Wong <markwkm@postgresql.org> writes:

Take 3. :)

I've upgraded everyone to the v14 buildfarm scripts and made sure the
--test passed on HEAD on each one. So I hopefully didn't miss any
(other than the one EOL OpenSUSE version that I will plan on upgrading.)

Thanks!

However ... it seems like most of your animals haven't run at all
in the last couple of days. Did you maybe forget to re-enable
their cron jobs after messing with them, or something like that?

Uh oh, more user error. I tested run_build but run_branches wasn't
happy. I'll start working through that...

I think I have most of them operational again. I see some animals are
still failing on some branches, but still better overall?

I discovered that clang for gadwall and pintail got purged when I
removed python2, so I disabled those two animals.

Regards,
Mark

#73Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#46)
4 attachment(s)
Re: Time to drop plpython2?

Hi,

On 2022-02-16 22:52:08 -0800, Andres Freund wrote:

On 2022-02-16 11:58:50 -0800, Andres Freund wrote:

Cool, will apply 1) later today.

Done. Curious how red the BF will turn out to be. Let's hope it's not
too bad.

Now that the BF has stabilized, I've rebased and cleaned up the patches I'd
posted earlier. Attached for the first time is my attempt at cleaning up the
docs.

0003, the removal of code level support for Python 2, is now a good bit bigger
bigger, due to the removal of the last remnants of the Py2/3 porting layer.

I did so far leave in the "major version conflict" detection stuff in
plpy_main.c - that could again be useful? I'm leaning towards removing it, I'd
hope that there's not again such a painful transition, and we have the git
history if needed.

Regards,

Andres

Attachments:

v6-0001-plpython-Remove-plpythonu-plpython2u-and-associat.patchtext/x-diff; charset=us-asciiDownload
From 8c85dd7e073d84e1a1f08f7948a195a5b9676ed6 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 6 Mar 2022 16:57:28 -0800
Subject: [PATCH v6 1/4] plpython: Remove plpythonu, plpython2u and associated
 transform extensions.

Reviewed-By: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 src/pl/plpython/plpython2u--1.0.sql           | 17 -----------------
 src/pl/plpython/plpython2u.control            |  7 -------
 src/pl/plpython/plpythonu--1.0.sql            | 17 -----------------
 src/pl/plpython/plpythonu.control             |  7 -------
 contrib/hstore_plpython/Makefile              |  4 ++--
 .../hstore_plpython2u--1.0.sql                | 19 -------------------
 .../hstore_plpython/hstore_plpython2u.control |  6 ------
 .../hstore_plpython/hstore_plpythonu--1.0.sql | 19 -------------------
 .../hstore_plpython/hstore_plpythonu.control  |  6 ------
 contrib/jsonb_plpython/Makefile               |  6 +++---
 .../jsonb_plpython/jsonb_plpython2u--1.0.sql  | 19 -------------------
 .../jsonb_plpython/jsonb_plpython2u.control   |  6 ------
 .../jsonb_plpython/jsonb_plpythonu--1.0.sql   | 19 -------------------
 .../jsonb_plpython/jsonb_plpythonu.control    |  6 ------
 contrib/ltree_plpython/Makefile               |  4 ++--
 .../ltree_plpython/ltree_plpython2u--1.0.sql  | 12 ------------
 .../ltree_plpython/ltree_plpython2u.control   |  6 ------
 .../ltree_plpython/ltree_plpythonu--1.0.sql   | 12 ------------
 .../ltree_plpython/ltree_plpythonu.control    |  6 ------
 19 files changed, 7 insertions(+), 191 deletions(-)
 delete mode 100644 src/pl/plpython/plpython2u--1.0.sql
 delete mode 100644 src/pl/plpython/plpython2u.control
 delete mode 100644 src/pl/plpython/plpythonu--1.0.sql
 delete mode 100644 src/pl/plpython/plpythonu.control
 delete mode 100644 contrib/hstore_plpython/hstore_plpython2u--1.0.sql
 delete mode 100644 contrib/hstore_plpython/hstore_plpython2u.control
 delete mode 100644 contrib/hstore_plpython/hstore_plpythonu--1.0.sql
 delete mode 100644 contrib/hstore_plpython/hstore_plpythonu.control
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpython2u.control
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpythonu.control
 delete mode 100644 contrib/ltree_plpython/ltree_plpython2u--1.0.sql
 delete mode 100644 contrib/ltree_plpython/ltree_plpython2u.control
 delete mode 100644 contrib/ltree_plpython/ltree_plpythonu--1.0.sql
 delete mode 100644 contrib/ltree_plpython/ltree_plpythonu.control

diff --git a/src/pl/plpython/plpython2u--1.0.sql b/src/pl/plpython/plpython2u--1.0.sql
deleted file mode 100644
index 69f74775678..00000000000
--- a/src/pl/plpython/plpython2u--1.0.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/* src/pl/plpython/plpython2u--1.0.sql */
-
-CREATE FUNCTION plpython2_call_handler() RETURNS language_handler
-  LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython2_inline_handler(internal) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython2_validator(oid) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE LANGUAGE plpython2u
-  HANDLER plpython2_call_handler
-  INLINE plpython2_inline_handler
-  VALIDATOR plpython2_validator;
-
-COMMENT ON LANGUAGE plpython2u IS 'PL/Python2U untrusted procedural language';
diff --git a/src/pl/plpython/plpython2u.control b/src/pl/plpython/plpython2u.control
deleted file mode 100644
index 39c2b791efe..00000000000
--- a/src/pl/plpython/plpython2u.control
+++ /dev/null
@@ -1,7 +0,0 @@
-# plpython2u extension
-comment = 'PL/Python2U untrusted procedural language'
-default_version = '1.0'
-module_pathname = '$libdir/plpython2'
-relocatable = false
-schema = pg_catalog
-superuser = true
diff --git a/src/pl/plpython/plpythonu--1.0.sql b/src/pl/plpython/plpythonu--1.0.sql
deleted file mode 100644
index 4c6f7c3f140..00000000000
--- a/src/pl/plpython/plpythonu--1.0.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/* src/pl/plpython/plpythonu--1.0.sql */
-
-CREATE FUNCTION plpython_call_handler() RETURNS language_handler
-  LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_inline_handler(internal) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_validator(oid) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE LANGUAGE plpythonu
-  HANDLER plpython_call_handler
-  INLINE plpython_inline_handler
-  VALIDATOR plpython_validator;
-
-COMMENT ON LANGUAGE plpythonu IS 'PL/PythonU untrusted procedural language';
diff --git a/src/pl/plpython/plpythonu.control b/src/pl/plpython/plpythonu.control
deleted file mode 100644
index ae91b1c255c..00000000000
--- a/src/pl/plpython/plpythonu.control
+++ /dev/null
@@ -1,7 +0,0 @@
-# plpythonu extension
-comment = 'PL/PythonU untrusted procedural language'
-default_version = '1.0'
-module_pathname = '$libdir/plpython2'
-relocatable = false
-schema = pg_catalog
-superuser = true
diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile
index 6af097ae68b..19d99a8045e 100644
--- a/contrib/hstore_plpython/Makefile
+++ b/contrib/hstore_plpython/Makefile
@@ -6,8 +6,8 @@ OBJS = \
 	hstore_plpython.o
 PGFILEDESC = "hstore_plpython - hstore transform for plpython"
 
-EXTENSION = hstore_plpythonu hstore_plpython2u hstore_plpython3u
-DATA = hstore_plpythonu--1.0.sql hstore_plpython2u--1.0.sql hstore_plpython3u--1.0.sql
+EXTENSION = hstore_plpython3u
+DATA = hstore_plpython3u--1.0.sql
 
 REGRESS = hstore_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
diff --git a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql b/contrib/hstore_plpython/hstore_plpython2u--1.0.sql
deleted file mode 100644
index 800765f3f0c..00000000000
--- a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/hstore_plpython/hstore_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION hstore_plpython2u" to load this file. \quit
-
-CREATE FUNCTION hstore_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'hstore_to_plpython';
-
-CREATE FUNCTION plpython2_to_hstore(val internal) RETURNS hstore
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'plpython_to_hstore';
-
-CREATE TRANSFORM FOR hstore LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION hstore_to_plpython2(internal),
-    TO SQL WITH FUNCTION plpython2_to_hstore(internal)
-);
-
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython2u IS 'transform between hstore and Python dict';
diff --git a/contrib/hstore_plpython/hstore_plpython2u.control b/contrib/hstore_plpython/hstore_plpython2u.control
deleted file mode 100644
index ed905671123..00000000000
--- a/contrib/hstore_plpython/hstore_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# hstore_plpython2u extension
-comment = 'transform between hstore and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/hstore_plpython2'
-relocatable = true
-requires = 'hstore,plpython2u'
diff --git a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql b/contrib/hstore_plpython/hstore_plpythonu--1.0.sql
deleted file mode 100644
index 52832912abc..00000000000
--- a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/hstore_plpython/hstore_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION hstore_plpythonu" to load this file. \quit
-
-CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR hstore LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION hstore_to_plpython(internal),
-    TO SQL WITH FUNCTION plpython_to_hstore(internal)
-);
-
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'transform between hstore and Python dict';
diff --git a/contrib/hstore_plpython/hstore_plpythonu.control b/contrib/hstore_plpython/hstore_plpythonu.control
deleted file mode 100644
index 8e9b35e43bf..00000000000
--- a/contrib/hstore_plpython/hstore_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# hstore_plpythonu extension
-comment = 'transform between hstore and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/hstore_plpython2'
-relocatable = true
-requires = 'hstore,plpythonu'
diff --git a/contrib/jsonb_plpython/Makefile b/contrib/jsonb_plpython/Makefile
index ca767418943..839b8f9a2fa 100644
--- a/contrib/jsonb_plpython/Makefile
+++ b/contrib/jsonb_plpython/Makefile
@@ -4,12 +4,12 @@ MODULE_big = jsonb_plpython$(python_majorversion)
 OBJS = \
 	$(WIN32RES) \
 	jsonb_plpython.o
-PGFILEDESC = "jsonb_plpython - transform between jsonb and plpythonu"
+PGFILEDESC = "jsonb_plpython - transform between jsonb and plpython3u"
 
 PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
-EXTENSION = jsonb_plpythonu jsonb_plpython2u jsonb_plpython3u
-DATA = jsonb_plpythonu--1.0.sql jsonb_plpython2u--1.0.sql jsonb_plpython3u--1.0.sql
+EXTENSION = jsonb_plpython3u
+DATA = jsonb_plpython3u--1.0.sql
 
 REGRESS = jsonb_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
diff --git a/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql b/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
deleted file mode 100644
index 2526d14ee19..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION jsonb_plpython2u" to load this file. \quit
-
-CREATE FUNCTION jsonb_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'jsonb_to_plpython';
-
-CREATE FUNCTION plpython2_to_jsonb(val internal) RETURNS jsonb
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'plpython_to_jsonb';
-
-CREATE TRANSFORM FOR jsonb LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION jsonb_to_plpython2(internal),
-    TO SQL WITH FUNCTION plpython2_to_jsonb(internal)
-);
-
-COMMENT ON TRANSFORM FOR jsonb LANGUAGE plpython2u IS 'transform between jsonb and Python';
diff --git a/contrib/jsonb_plpython/jsonb_plpython2u.control b/contrib/jsonb_plpython/jsonb_plpython2u.control
deleted file mode 100644
index d26368316b6..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# jsonb_plpython2u extension
-comment = 'transform between jsonb and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/jsonb_plpython2'
-relocatable = true
-requires = 'plpython2u'
diff --git a/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql b/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
deleted file mode 100644
index 3fa89885a63..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION jsonb_plpythonu" to load this file. \quit
-
-CREATE FUNCTION jsonb_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_to_jsonb(val internal) RETURNS jsonb
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR jsonb LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION jsonb_to_plpython(internal),
-    TO SQL WITH FUNCTION plpython_to_jsonb(internal)
-);
-
-COMMENT ON TRANSFORM FOR jsonb LANGUAGE plpythonu IS 'transform between jsonb and Python';
diff --git a/contrib/jsonb_plpython/jsonb_plpythonu.control b/contrib/jsonb_plpython/jsonb_plpythonu.control
deleted file mode 100644
index 6f8fa4f184b..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# jsonb_plpythonu extension
-comment = 'transform between jsonb and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/jsonb_plpython2'
-relocatable = true
-requires = 'plpythonu'
diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile
index 12a01467721..0bccb111e6b 100644
--- a/contrib/ltree_plpython/Makefile
+++ b/contrib/ltree_plpython/Makefile
@@ -6,8 +6,8 @@ OBJS = \
 	ltree_plpython.o
 PGFILEDESC = "ltree_plpython - ltree transform for plpython"
 
-EXTENSION = ltree_plpythonu ltree_plpython2u ltree_plpython3u
-DATA = ltree_plpythonu--1.0.sql ltree_plpython2u--1.0.sql ltree_plpython3u--1.0.sql
+EXTENSION = ltree_plpython3u
+DATA = ltree_plpython3u--1.0.sql
 
 REGRESS = ltree_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
diff --git a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql b/contrib/ltree_plpython/ltree_plpython2u--1.0.sql
deleted file mode 100644
index 5c4a7037013..00000000000
--- a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/* contrib/ltree_plpython/ltree_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION ltree_plpython2u" to load this file. \quit
-
-CREATE FUNCTION ltree_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'ltree_to_plpython';
-
-CREATE TRANSFORM FOR ltree LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION ltree_to_plpython2(internal)
-);
diff --git a/contrib/ltree_plpython/ltree_plpython2u.control b/contrib/ltree_plpython/ltree_plpython2u.control
deleted file mode 100644
index bedfd0acbad..00000000000
--- a/contrib/ltree_plpython/ltree_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# ltree_plpython2u extension
-comment = 'transform between ltree and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/ltree_plpython2'
-relocatable = true
-requires = 'ltree,plpython2u'
diff --git a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql b/contrib/ltree_plpython/ltree_plpythonu--1.0.sql
deleted file mode 100644
index ee93edf28b9..00000000000
--- a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/* contrib/ltree_plpython/ltree_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION ltree_plpythonu" to load this file. \quit
-
-CREATE FUNCTION ltree_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR ltree LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION ltree_to_plpython(internal)
-);
diff --git a/contrib/ltree_plpython/ltree_plpythonu.control b/contrib/ltree_plpython/ltree_plpythonu.control
deleted file mode 100644
index b03c89a2e6e..00000000000
--- a/contrib/ltree_plpython/ltree_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# ltree_plpythonu extension
-comment = 'transform between ltree and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/ltree_plpython2'
-relocatable = true
-requires = 'ltree,plpythonu'
-- 
2.35.1.354.g715d08a9e5

v6-0002-plpython-Remove-regression-test-infrastructure-fo.patchtext/x-diff; charset=us-asciiDownload
From c726eba9599b3f5aa86da399c349e7afe3d17b68 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 6 Mar 2022 16:57:28 -0800
Subject: [PATCH v6 2/4] plpython: Remove regression test infrastructure for
 Python 2.

Reviewed-By: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 src/pl/plpython/.gitignore                    |    2 -
 src/pl/plpython/Makefile                      |   14 -
 src/pl/plpython/expected/plpython_call.out    |   12 +-
 .../plpython/expected/plpython_composite.out  |   32 +-
 src/pl/plpython/expected/plpython_do.out      |    8 +-
 src/pl/plpython/expected/plpython_drop.out    |    3 +-
 src/pl/plpython/expected/plpython_ereport.out |   37 +-
 src/pl/plpython/expected/plpython_error.out   |   52 +-
 src/pl/plpython/expected/plpython_error_5.out |  447 --------
 src/pl/plpython/expected/plpython_global.out  |    6 +-
 src/pl/plpython/expected/plpython_import.out  |    8 +-
 src/pl/plpython/expected/plpython_newline.out |    6 +-
 src/pl/plpython/expected/plpython_params.out  |    8 +-
 src/pl/plpython/expected/plpython_quote.out   |    2 +-
 src/pl/plpython/expected/plpython_record.out  |   18 +-
 src/pl/plpython/expected/plpython_setof.out   |   18 +-
 src/pl/plpython/expected/plpython_spi.out     |   50 +-
 .../expected/plpython_subtransaction.out      |   38 +-
 src/pl/plpython/expected/plpython_test.out    |   12 +-
 .../expected/plpython_transaction.out         |   28 +-
 src/pl/plpython/expected/plpython_trigger.out |   46 +-
 src/pl/plpython/expected/plpython_types.out   |  230 ++--
 src/pl/plpython/expected/plpython_types_3.out | 1009 -----------------
 src/pl/plpython/expected/plpython_unicode.out |   16 +-
 src/pl/plpython/expected/plpython_void.out    |    6 +-
 src/pl/plpython/regress-python3-mangle.mk     |   38 -
 src/pl/plpython/sql/plpython_call.sql         |   12 +-
 src/pl/plpython/sql/plpython_composite.sql    |   32 +-
 src/pl/plpython/sql/plpython_do.sql           |    6 +-
 src/pl/plpython/sql/plpython_drop.sql         |    4 +-
 src/pl/plpython/sql/plpython_ereport.sql      |   26 +-
 src/pl/plpython/sql/plpython_error.sql        |   48 +-
 src/pl/plpython/sql/plpython_global.sql       |    6 +-
 src/pl/plpython/sql/plpython_import.sql       |    8 +-
 src/pl/plpython/sql/plpython_newline.sql      |    6 +-
 src/pl/plpython/sql/plpython_params.sql       |    8 +-
 src/pl/plpython/sql/plpython_quote.sql        |    2 +-
 src/pl/plpython/sql/plpython_record.sql       |   18 +-
 src/pl/plpython/sql/plpython_setof.sql        |   18 +-
 src/pl/plpython/sql/plpython_spi.sql          |   50 +-
 .../plpython/sql/plpython_subtransaction.sql  |   38 +-
 src/pl/plpython/sql/plpython_test.sql         |   12 +-
 src/pl/plpython/sql/plpython_transaction.sql  |   26 +-
 src/pl/plpython/sql/plpython_trigger.sql      |   46 +-
 src/pl/plpython/sql/plpython_types.sql        |  106 +-
 src/pl/plpython/sql/plpython_unicode.sql      |   16 +-
 src/pl/plpython/sql/plpython_void.sql         |    6 +-
 contrib/hstore_plpython/.gitignore            |    2 -
 contrib/hstore_plpython/Makefile              |    6 -
 .../expected/hstore_plpython.out              |   22 +-
 .../hstore_plpython/sql/hstore_plpython.sql   |   18 +-
 contrib/jsonb_plpython/.gitignore             |    2 -
 contrib/jsonb_plpython/Makefile               |    7 -
 .../expected/jsonb_plpython.out               |   32 +-
 contrib/jsonb_plpython/sql/jsonb_plpython.sql |   30 +-
 contrib/ltree_plpython/.gitignore             |    2 -
 contrib/ltree_plpython/Makefile               |    6 -
 .../expected/ltree_plpython.out               |   10 +-
 contrib/ltree_plpython/sql/ltree_plpython.sql |    8 +-
 src/tools/msvc/vcregress.pl                   |   76 +-
 60 files changed, 625 insertions(+), 2236 deletions(-)
 delete mode 100644 src/pl/plpython/expected/plpython_error_5.out
 delete mode 100644 src/pl/plpython/expected/plpython_types_3.out
 delete mode 100644 src/pl/plpython/regress-python3-mangle.mk

diff --git a/src/pl/plpython/.gitignore b/src/pl/plpython/.gitignore
index 70c08db3231..07bee6a29c4 100644
--- a/src/pl/plpython/.gitignore
+++ b/src/pl/plpython/.gitignore
@@ -1,7 +1,5 @@
 /spiexceptions.h
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index a83ae8865c7..0d6f74de71f 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -35,9 +35,6 @@ OBJS = \
 	plpy_util.o
 
 DATA = $(NAME)u.control $(NAME)u--1.0.sql
-ifeq ($(python_majorversion),2)
-DATA += plpythonu.control plpythonu--1.0.sql
-endif
 
 # header files to install - it's not clear which of these might be needed
 # so install them all.
@@ -77,11 +74,6 @@ endif # win32
 SHLIB_LINK = $(python_libspec) $(python_additional_libs) $(filter -lintl,$(LIBS))
 
 REGRESS_OPTS = --dbname=$(PL_TESTDB)
-# Only load plpythonu with Python 2.  The test files themselves load
-# the versioned language plpython(2|3)u.
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu
-endif
 
 REGRESS = \
 	plpython_schema \
@@ -108,8 +100,6 @@ REGRESS = \
 	plpython_transaction \
 	plpython_drop
 
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
-
 include $(top_srcdir)/src/Makefile.shlib
 
 all: all-lib
@@ -127,7 +117,6 @@ uninstall: uninstall-lib uninstall-data
 install-data: installdirs
 	$(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA)) '$(DESTDIR)$(datadir)/extension/'
 	$(INSTALL_DATA) $(addprefix $(srcdir)/, $(INCS)) '$(DESTDIR)$(includedir_server)'
-	$(INSTALL_DATA) $(srcdir)/regress-python3-mangle.mk '$(DESTDIR)$(pgxsdir)/src/pl/plpython'
 
 uninstall-data:
 	rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
@@ -136,9 +125,6 @@ uninstall-data:
 .PHONY: install-data uninstall-data
 
 
-include $(srcdir)/regress-python3-mangle.mk
-
-
 check: submake-pg-regress
 	$(pg_regress_check) $(REGRESS_OPTS) $(REGRESS)
 
diff --git a/src/pl/plpython/expected/plpython_call.out b/src/pl/plpython/expected/plpython_call.out
index 55e1027246a..4c0690067a0 100644
--- a/src/pl/plpython/expected/plpython_call.out
+++ b/src/pl/plpython/expected/plpython_call.out
@@ -2,14 +2,14 @@
 -- Tests for procedures / CALL syntax
 --
 CREATE PROCEDURE test_proc1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 pass
 $$;
 CALL test_proc1();
 -- error: can't return non-None
 CREATE PROCEDURE test_proc2()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return 5
 $$;
@@ -18,7 +18,7 @@ ERROR:  PL/Python procedure did not return None
 CONTEXT:  PL/Python procedure "test_proc2"
 CREATE TABLE test1 (a int);
 CREATE PROCEDURE test_proc3(x int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("INSERT INTO test1 VALUES (%s)" % x)
 $$;
@@ -31,7 +31,7 @@ SELECT * FROM test1;
 
 -- output arguments
 CREATE PROCEDURE test_proc5(INOUT a text)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return [a + '+' + a]
 $$;
@@ -42,7 +42,7 @@ CALL test_proc5('abc');
 (1 row)
 
 CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return (b * a, c * a)
 $$;
@@ -54,7 +54,7 @@ CALL test_proc6(2, 3, 4);
 
 -- OUT parameters
 CREATE PROCEDURE test_proc9(IN a int, OUT b int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.notice("a: %s" % (a))
 return (a * 2,)
diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out
index af801923343..bb101e07d53 100644
--- a/src/pl/plpython/expected/plpython_composite.out
+++ b/src/pl/plpython/expected/plpython_composite.out
@@ -1,6 +1,6 @@
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT multiout_simple();
  multiout_simple 
 -----------------
@@ -27,7 +27,7 @@ SELECT (multiout_simple()).j + 3;
 
 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT multiout_simple_setof();
  multiout_simple_setof 
 -----------------------
@@ -67,7 +67,7 @@ elif typ == 'obj':
     return type_record
 elif typ == 'str':
     return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
  first | second 
 -------+--------
@@ -237,7 +237,7 @@ for i in range(n):
     power = 2 ** i
     length = plpy.execute("select length('%d')" % power)[0]['length']
     yield power, length
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_setof(3);
  power_of_2 | length 
 ------------+--------
@@ -260,7 +260,7 @@ CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
 return [{'x': 4, 'y' :'four'},
         {'x': 7, 'y' :'seven'},
         {'x': 0, 'y' :'zero'}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_return_table();
  x |   y   
 ---+-------
@@ -273,7 +273,7 @@ CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS
 yield [[1], 'a']
 yield [[1,2], 'b']
 yield [[1,2,3], None]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_array();
  column1 | column2 
 ---------+---------
@@ -284,11 +284,11 @@ SELECT * FROM multiout_array();
 
 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
 return {'first': 1, 'second': 2}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
 return [{'first': 1, 'second': 2},
        {'first': 3, 'second': 4	}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM singleout_composite();
  first | second 
 -------+--------
@@ -305,7 +305,7 @@ SELECT * FROM multiout_composite();
 -- composite OUT parameters in functions returning RECORD not supported yet
 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
 return (n, (n * 2, n * 3))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
 if returnnull:
     d = None
@@ -323,7 +323,7 @@ elif typ == 'str':
     d = "(%r,%r)" % (n * 2, n * 3)
 for i in range(n):
     yield (i, d)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_composite(2);
  n | column2 
 ---+---------
@@ -438,7 +438,7 @@ CREATE TABLE changing (
 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
 return [(1, {'i': 1, 'j': 2}),
         (1, (3, 4))]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM changing_test();
  n | column2 
 ---+---------
@@ -474,7 +474,7 @@ yield {'tab': [('first', 1), ('second', 2)],
 yield {'tab': [('first', 1), ('second', 2)],
       'typ': [{'first': 'third', 'second': 3},
               {'first': 'fourth', 'second': 4}]}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_types_table();
             tab             |            typ             
 ----------------------------+----------------------------
@@ -486,7 +486,7 @@ SELECT * FROM composite_types_table();
 -- check what happens if the output record descriptor changes
 CREATE FUNCTION return_record(t text) RETURNS record AS $$
 return {'t': t, 'val': 10}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM return_record('abc') AS r(t text, val integer);
   t  | val 
 -----+-----
@@ -525,7 +525,7 @@ SELECT * FROM return_record('999') AS r(val text, t integer);
 
 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
 return {'v1':1,'v2':2,t:3}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
  v3 | v2 | v1 
 ----+----+----
@@ -572,7 +572,7 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
 -- multi-dimensional array of composite types.
 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list();
                                composite_type_as_list                               
 ------------------------------------------------------------------------------------
@@ -585,7 +585,7 @@ SELECT * FROM composite_type_as_list();
 -- on the issue.
 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
   return [['first', 1]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list_broken();
 ERROR:  malformed record literal: "first"
 DETAIL:  Missing left parenthesis.
diff --git a/src/pl/plpython/expected/plpython_do.out b/src/pl/plpython/expected/plpython_do.out
index e300530e031..d131a4c0ed6 100644
--- a/src/pl/plpython/expected/plpython_do.out
+++ b/src/pl/plpython/expected/plpython_do.out
@@ -1,8 +1,6 @@
-DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
-NOTICE:  This is plpythonu.
-DO $$ plpy.notice("This is plpython2u.") $$ LANGUAGE plpython2u;
-NOTICE:  This is plpython2u.
-DO $$ raise Exception("error test") $$ LANGUAGE plpythonu;
+DO $$ plpy.notice("This is plpython3u.") $$ LANGUAGE plpython3u;
+NOTICE:  This is plpython3u.
+DO $$ raise Exception("error test") $$ LANGUAGE plpython3u;
 ERROR:  Exception: error test
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
diff --git a/src/pl/plpython/expected/plpython_drop.out b/src/pl/plpython/expected/plpython_drop.out
index a0e3b5c4ef6..97bb54a55e7 100644
--- a/src/pl/plpython/expected/plpython_drop.out
+++ b/src/pl/plpython/expected/plpython_drop.out
@@ -2,5 +2,4 @@
 -- For paranoia's sake, don't leave an untrusted language sitting around
 --
 SET client_min_messages = WARNING;
-DROP EXTENSION plpythonu CASCADE;
-DROP EXTENSION IF EXISTS plpython2u CASCADE;
+DROP EXTENSION plpython3u CASCADE;
diff --git a/src/pl/plpython/expected/plpython_ereport.out b/src/pl/plpython/expected/plpython_ereport.out
index b73bfff5115..74dcc419ceb 100644
--- a/src/pl/plpython/expected/plpython_ereport.out
+++ b/src/pl/plpython/expected/plpython_ereport.out
@@ -17,7 +17,7 @@ plpy.info('This is message text.',
 plpy.notice('notice', detail='some detail')
 plpy.warning('warning', detail='some detail')
 plpy.error('stop on error', detail='some detail', hint='some hint')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT elog_test();
 INFO:  info
 DETAIL:  some detail
@@ -38,42 +38,42 @@ CONTEXT:  Traceback (most recent call last):
   PL/Python function "elog_test", line 18, in <module>
     plpy.error('stop on error', detail='some detail', hint='some hint')
 PL/Python function "elog_test"
-DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpythonu;
+DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  (10, 20)
 DO $$
 import time;
 from datetime import date
 plpy.info('other types', detail=date(2016, 2, 26))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  2016-02-26
 DO $$
 basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 plpy.info('other types', detail=basket)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 -- should fail
-DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u;
 ERROR:  ValueError: invalid SQLSTATE code
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('wrong sqlstate', sqlstate='54444A') 
 PL/Python anonymous code block
-DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: 'blabla' is an invalid keyword argument for this function
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('unsupported argument', blabla='fooboo') 
 PL/Python anonymous code block
-DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: argument 'message' given by name and position
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('first message', message='second message') 
 PL/Python anonymous code block
-DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: argument 'message' given by name and position
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
@@ -96,7 +96,7 @@ kwargs = {
 }
 # ignore None values
 plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT raise_exception('hello', 'world');
 ERROR:  plpy.Error: hello
 DETAIL:  world
@@ -180,26 +180,35 @@ END;
 $$;
 NOTICE:  handled exception
 DETAIL:  message:(plpy.Error: message text), detail:(detail text), hint: (hint text), sqlstate: (XX555), schema_name:(schema text), table_name:(table text), column_name:(column text), datatype_name:(datatype text), constraint_name:(constraint text)
--- The displayed context is different between Python2 and Python3,
--- but that's not important for this test.
-\set SHOW_CONTEXT never
 DO $$
 try:
     plpy.execute("select raise_exception(_message => 'my message', _sqlstate => 'XX987', _hint => 'some hint', _table_name => 'users_tab', _datatype_name => 'user_type')")
 except Exception as e:
     plpy.info(e.spidata)
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  (119577128, None, 'some hint', None, 0, None, 'users_tab', None, 'user_type', None)
 ERROR:  plpy.SPIError: plpy.Error: my message
 HINT:  some hint
+CONTEXT:  Traceback (most recent call last):
+  PL/Python anonymous code block, line 6, in <module>
+    raise e
+  PL/Python anonymous code block, line 3, in __plpython_inline_block
+    plpy.execute("select raise_exception(_message => 'my message', _sqlstate => 'XX987', _hint => 'some hint', _table_name => 'users_tab', _datatype_name => 'user_type')")
+PL/Python anonymous code block
 DO $$
 try:
     plpy.error(message  = 'my message', sqlstate = 'XX987', hint = 'some hint', table_name = 'users_tab', datatype_name = 'user_type')
 except Exception as e:
     plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name))
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  sqlstate: XX987, hint: some hint, table_name: users_tab, datatype_name: user_type
 ERROR:  plpy.Error: my message
 HINT:  some hint
+CONTEXT:  Traceback (most recent call last):
+  PL/Python anonymous code block, line 6, in <module>
+    raise e
+  PL/Python anonymous code block, line 3, in __plpython_inline_block
+    plpy.error(message  = 'my message', sqlstate = 'XX987', hint = 'some hint', table_name = 'users_tab', datatype_name = 'user_type')
+PL/Python anonymous code block
diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index b2f8fe83eb6..7fe864a1a57 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -6,7 +6,7 @@
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 ERROR:  could not compile PL/Python function "python_syntax_error"
 DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
 /* With check_function_bodies = false the function should get defined
@@ -16,7 +16,7 @@ SET check_function_bodies = false;
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 SELECT python_syntax_error();
 ERROR:  could not compile PL/Python function "python_syntax_error"
 DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
@@ -30,7 +30,7 @@ RESET check_function_bodies;
 CREATE FUNCTION sql_syntax_error() RETURNS text
         AS
 'plpy.execute("syntax error")'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 SELECT sql_syntax_error();
 ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
 LINE 1: syntax error
@@ -45,7 +45,7 @@ PL/Python function "sql_syntax_error"
 CREATE FUNCTION exception_index_invalid(text) RETURNS text
 	AS
 'return args[1]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT exception_index_invalid('test');
 ERROR:  IndexError: list index out of range
 CONTEXT:  Traceback (most recent call last):
@@ -58,7 +58,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text
 	AS
 'rv = plpy.execute("SELECT test5(''foo'')")
 return rv[0]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT exception_index_invalid_nested();
 ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
 LINE 1: SELECT test5('foo')
@@ -81,7 +81,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_uncaught('rick');
 ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
 CONTEXT:  Traceback (most recent call last):
@@ -105,7 +105,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_caught('rick');
 NOTICE:  type "test" does not exist
  invalid_type_caught 
@@ -129,7 +129,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_reraised('rick');
 ERROR:  plpy.Error: type "test" does not exist
 CONTEXT:  Traceback (most recent call last):
@@ -147,7 +147,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT valid_type('rick');
  valid_type 
 ------------
@@ -170,7 +170,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_error();
 ERROR:  plpy.Error: boom
 CONTEXT:  Traceback (most recent call last):
@@ -199,7 +199,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_error_raise();
 ERROR:  plpy.Error: boom
 CONTEXT:  Traceback (most recent call last):
@@ -228,7 +228,7 @@ def fun3():
 fun3()
 return "you''ve been warned"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_warning();
 WARNING:  boom
    nested_warning   
@@ -241,9 +241,9 @@ WARNING:  boom
 CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
 $$
 plpy.nonexistent
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT toplevel_attribute_error();
-ERROR:  AttributeError: 'module' object has no attribute 'nonexistent'
+ERROR:  AttributeError: module 'plpy' has no attribute 'nonexistent'
 CONTEXT:  Traceback (most recent call last):
   PL/Python function "toplevel_attribute_error", line 2, in <module>
     plpy.nonexistent
@@ -261,7 +261,7 @@ def third():
   plpy.execute("select sql_error()")
 
 first()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
 begin
   select 1/0;
@@ -274,7 +274,7 @@ end
 $$ LANGUAGE plpgsql;
 CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
 plpy.execute("select sql_error()")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT python_traceback();
 ERROR:  spiexceptions.DivisionByZero: division by zero
 CONTEXT:  Traceback (most recent call last):
@@ -325,7 +325,7 @@ except spiexceptions.NotNullViolation as e:
     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
 except spiexceptions.UniqueViolation as e:
     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT specific_exception(2);
  specific_exception 
 --------------------
@@ -351,7 +351,7 @@ NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
 CREATE FUNCTION python_unique_violation() RETURNS void AS $$
 plpy.execute("insert into specific values (1)")
 plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
 begin
     begin
@@ -374,7 +374,7 @@ CREATE FUNCTION manual_subxact() RETURNS void AS $$
 plpy.execute("savepoint save")
 plpy.execute("create table foo(x integer)")
 plpy.execute("rollback to save")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT manual_subxact();
 ERROR:  plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
 CONTEXT:  Traceback (most recent call last):
@@ -389,7 +389,7 @@ rollback = plpy.prepare("rollback to save")
 plpy.execute(save)
 plpy.execute("create table foo(x integer)")
 plpy.execute(rollback)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT manual_subxact_prepared();
 ERROR:  plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
 CONTEXT:  Traceback (most recent call last):
@@ -400,7 +400,7 @@ PL/Python function "manual_subxact_prepared"
  */
 CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
 raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 DO $$
 BEGIN
 	SELECT plpy_raise_spiexception();
@@ -414,7 +414,7 @@ CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
 exc = plpy.spiexceptions.DivisionByZero()
 exc.sqlstate = 'SILLY'
 raise exc
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 DO $$
 BEGIN
 	SELECT plpy_raise_spiexception_override();
@@ -425,18 +425,18 @@ $$ LANGUAGE plpgsql;
 /* test the context stack trace for nested execution levels
  */
 CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
 plpy.execute("SELECT notice_innerfunc()")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 \set SHOW_CONTEXT always
 SELECT notice_outerfunc();
 NOTICE:  inside DO
 CONTEXT:  PL/Python anonymous code block
-SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
+SQL statement "DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$"
 PL/Python function "notice_innerfunc"
 SQL statement "SELECT notice_innerfunc()"
 PL/Python function "notice_outerfunc"
diff --git a/src/pl/plpython/expected/plpython_error_5.out b/src/pl/plpython/expected/plpython_error_5.out
deleted file mode 100644
index bc66ab55340..00000000000
--- a/src/pl/plpython/expected/plpython_error_5.out
+++ /dev/null
@@ -1,447 +0,0 @@
--- test error handling, i forgot to restore Warn_restart in
--- the trigger handler once. the errors and subsequent core dump were
--- interesting.
-/* Flat out Python syntax error
- */
-CREATE FUNCTION python_syntax_error() RETURNS text
-        AS
-'.syntaxerror'
-        LANGUAGE plpython3u;
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-/* With check_function_bodies = false the function should get defined
- * and the error reported when called
- */
-SET check_function_bodies = false;
-CREATE FUNCTION python_syntax_error() RETURNS text
-        AS
-'.syntaxerror'
-        LANGUAGE plpython3u;
-SELECT python_syntax_error();
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-/* Run the function twice to check if the hashtable entry gets cleaned up */
-SELECT python_syntax_error();
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-RESET check_function_bodies;
-/* Flat out syntax error
- */
-CREATE FUNCTION sql_syntax_error() RETURNS text
-        AS
-'plpy.execute("syntax error")'
-        LANGUAGE plpython3u;
-SELECT sql_syntax_error();
-ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
-LINE 1: syntax error
-        ^
-QUERY:  syntax error
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "sql_syntax_error", line 1, in <module>
-    plpy.execute("syntax error")
-PL/Python function "sql_syntax_error"
-/* check the handling of uncaught python exceptions
- */
-CREATE FUNCTION exception_index_invalid(text) RETURNS text
-	AS
-'return args[1]'
-	LANGUAGE plpython3u;
-SELECT exception_index_invalid('test');
-ERROR:  IndexError: list index out of range
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "exception_index_invalid", line 1, in <module>
-    return args[1]
-PL/Python function "exception_index_invalid"
-/* check handling of nested exceptions
- */
-CREATE FUNCTION exception_index_invalid_nested() RETURNS text
-	AS
-'rv = plpy.execute("SELECT test5(''foo'')")
-return rv[0]'
-	LANGUAGE plpython3u;
-SELECT exception_index_invalid_nested();
-ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
-LINE 1: SELECT test5('foo')
-               ^
-HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
-QUERY:  SELECT test5('foo')
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "exception_index_invalid_nested", line 1, in <module>
-    rv = plpy.execute("SELECT test5('foo')")
-PL/Python function "exception_index_invalid_nested"
-/* a typo
- */
-CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	SD["plan"] = plpy.prepare(q, [ "test" ])
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_uncaught('rick');
-ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "invalid_type_uncaught", line 3, in <module>
-    SD["plan"] = plpy.prepare(q, [ "test" ])
-PL/Python function "invalid_type_uncaught"
-/* for what it's worth catch the exception generated by
- * the typo, and return None
- */
-CREATE FUNCTION invalid_type_caught(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	try:
-		SD["plan"] = plpy.prepare(q, [ "test" ])
-	except plpy.SPIError as ex:
-		plpy.notice(str(ex))
-		return None
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_caught('rick');
-NOTICE:  type "test" does not exist
- invalid_type_caught 
----------------------
- 
-(1 row)
-
-/* for what it's worth catch the exception generated by
- * the typo, and reraise it as a plain error
- */
-CREATE FUNCTION invalid_type_reraised(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	try:
-		SD["plan"] = plpy.prepare(q, [ "test" ])
-	except plpy.SPIError as ex:
-		plpy.error(str(ex))
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_reraised('rick');
-ERROR:  plpy.Error: type "test" does not exist
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "invalid_type_reraised", line 6, in <module>
-    plpy.error(str(ex))
-PL/Python function "invalid_type_reraised"
-/* no typo no messing about
- */
-CREATE FUNCTION valid_type(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ])
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT valid_type('rick');
- valid_type 
-------------
- 
-(1 row)
-
-/* error in nested functions to get a traceback
-*/
-CREATE FUNCTION nested_error() RETURNS text
-	AS
-'def fun1():
-	plpy.error("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "not reached"
-'
-	LANGUAGE plpython3u;
-SELECT nested_error();
-ERROR:  plpy.Error: boom
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "nested_error", line 10, in <module>
-    fun3()
-  PL/Python function "nested_error", line 8, in fun3
-    fun2()
-  PL/Python function "nested_error", line 5, in fun2
-    fun1()
-  PL/Python function "nested_error", line 2, in fun1
-    plpy.error("boom")
-PL/Python function "nested_error"
-/* raising plpy.Error is just like calling plpy.error
-*/
-CREATE FUNCTION nested_error_raise() RETURNS text
-	AS
-'def fun1():
-	raise plpy.Error("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "not reached"
-'
-	LANGUAGE plpython3u;
-SELECT nested_error_raise();
-ERROR:  plpy.Error: boom
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "nested_error_raise", line 10, in <module>
-    fun3()
-  PL/Python function "nested_error_raise", line 8, in fun3
-    fun2()
-  PL/Python function "nested_error_raise", line 5, in fun2
-    fun1()
-  PL/Python function "nested_error_raise", line 2, in fun1
-    raise plpy.Error("boom")
-PL/Python function "nested_error_raise"
-/* using plpy.warning should not produce a traceback
-*/
-CREATE FUNCTION nested_warning() RETURNS text
-	AS
-'def fun1():
-	plpy.warning("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "you''ve been warned"
-'
-	LANGUAGE plpython3u;
-SELECT nested_warning();
-WARNING:  boom
-   nested_warning   
---------------------
- you've been warned
-(1 row)
-
-/* AttributeError at toplevel used to give segfaults with the traceback
-*/
-CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
-$$
-plpy.nonexistent
-$$ LANGUAGE plpython3u;
-SELECT toplevel_attribute_error();
-ERROR:  AttributeError: module 'plpy' has no attribute 'nonexistent'
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "toplevel_attribute_error", line 2, in <module>
-    plpy.nonexistent
-PL/Python function "toplevel_attribute_error"
-/* Calling PL/Python functions from SQL and vice versa should not lose context.
- */
-CREATE OR REPLACE FUNCTION python_traceback() RETURNS void AS $$
-def first():
-  second()
-
-def second():
-  third()
-
-def third():
-  plpy.execute("select sql_error()")
-
-first()
-$$ LANGUAGE plpython3u;
-CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
-begin
-  select 1/0;
-end
-$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION python_from_sql_error() RETURNS void AS $$
-begin
-  select python_traceback();
-end
-$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
-plpy.execute("select sql_error()")
-$$ LANGUAGE plpython3u;
-SELECT python_traceback();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "python_traceback", line 11, in <module>
-    first()
-  PL/Python function "python_traceback", line 3, in first
-    second()
-  PL/Python function "python_traceback", line 6, in second
-    third()
-  PL/Python function "python_traceback", line 9, in third
-    plpy.execute("select sql_error()")
-PL/Python function "python_traceback"
-SELECT sql_error();
-ERROR:  division by zero
-CONTEXT:  SQL statement "select 1/0"
-PL/pgSQL function sql_error() line 3 at SQL statement
-SELECT python_from_sql_error();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "python_traceback", line 11, in <module>
-    first()
-  PL/Python function "python_traceback", line 3, in first
-    second()
-  PL/Python function "python_traceback", line 6, in second
-    third()
-  PL/Python function "python_traceback", line 9, in third
-    plpy.execute("select sql_error()")
-PL/Python function "python_traceback"
-SQL statement "select python_traceback()"
-PL/pgSQL function python_from_sql_error() line 3 at SQL statement
-SELECT sql_from_python_error();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "sql_from_python_error", line 2, in <module>
-    plpy.execute("select sql_error()")
-PL/Python function "sql_from_python_error"
-/* check catching specific types of exceptions
- */
-CREATE TABLE specific (
-    i integer PRIMARY KEY
-);
-CREATE FUNCTION specific_exception(i integer) RETURNS void AS
-$$
-from plpy import spiexceptions
-try:
-    plpy.execute("insert into specific values (%s)" % (i or "NULL"));
-except spiexceptions.NotNullViolation as e:
-    plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
-except spiexceptions.UniqueViolation as e:
-    plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpython3u;
-SELECT specific_exception(2);
- specific_exception 
---------------------
- 
-(1 row)
-
-SELECT specific_exception(NULL);
-NOTICE:  Violated the NOT NULL constraint, sqlstate 23502
- specific_exception 
---------------------
- 
-(1 row)
-
-SELECT specific_exception(2);
-NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
- specific_exception 
---------------------
- 
-(1 row)
-
-/* SPI errors in PL/Python functions should preserve the SQLSTATE value
- */
-CREATE FUNCTION python_unique_violation() RETURNS void AS $$
-plpy.execute("insert into specific values (1)")
-plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpython3u;
-CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
-begin
-    begin
-        perform python_unique_violation();
-    exception when unique_violation then
-        return 'ok';
-    end;
-    return 'not reached';
-end;
-$$ language plpgsql;
-SELECT catch_python_unique_violation();
- catch_python_unique_violation 
--------------------------------
- ok
-(1 row)
-
-/* manually starting subtransactions - a bad idea
- */
-CREATE FUNCTION manual_subxact() RETURNS void AS $$
-plpy.execute("savepoint save")
-plpy.execute("create table foo(x integer)")
-plpy.execute("rollback to save")
-$$ LANGUAGE plpython3u;
-SELECT manual_subxact();
-ERROR:  plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "manual_subxact", line 2, in <module>
-    plpy.execute("savepoint save")
-PL/Python function "manual_subxact"
-/* same for prepared plans
- */
-CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
-save = plpy.prepare("savepoint save")
-rollback = plpy.prepare("rollback to save")
-plpy.execute(save)
-plpy.execute("create table foo(x integer)")
-plpy.execute(rollback)
-$$ LANGUAGE plpython3u;
-SELECT manual_subxact_prepared();
-ERROR:  plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "manual_subxact_prepared", line 4, in <module>
-    plpy.execute(save)
-PL/Python function "manual_subxact_prepared"
-/* raising plpy.spiexception.* from python code should preserve sqlstate
- */
-CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
-raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpython3u;
-DO $$
-BEGIN
-	SELECT plpy_raise_spiexception();
-EXCEPTION WHEN division_by_zero THEN
-	-- NOOP
-END
-$$ LANGUAGE plpgsql;
-/* setting a custom sqlstate should be handled
- */
-CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
-exc = plpy.spiexceptions.DivisionByZero()
-exc.sqlstate = 'SILLY'
-raise exc
-$$ LANGUAGE plpython3u;
-DO $$
-BEGIN
-	SELECT plpy_raise_spiexception_override();
-EXCEPTION WHEN SQLSTATE 'SILLY' THEN
-	-- NOOP
-END
-$$ LANGUAGE plpgsql;
-/* test the context stack trace for nested execution levels
- */
-CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
-return 1
-$$ LANGUAGE plpythonu;
-CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
-plpy.execute("SELECT notice_innerfunc()")
-return 1
-$$ LANGUAGE plpythonu;
-\set SHOW_CONTEXT always
-SELECT notice_outerfunc();
-NOTICE:  inside DO
-CONTEXT:  PL/Python anonymous code block
-SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
-PL/Python function "notice_innerfunc"
-SQL statement "SELECT notice_innerfunc()"
-PL/Python function "notice_outerfunc"
- notice_outerfunc 
-------------------
-                1
-(1 row)
-
diff --git a/src/pl/plpython/expected/plpython_global.out b/src/pl/plpython/expected/plpython_global.out
index 192e3e48a72..a4cfb1483f9 100644
--- a/src/pl/plpython/expected/plpython_global.out
+++ b/src/pl/plpython/expected/plpython_global.out
@@ -8,7 +8,7 @@ CREATE FUNCTION global_test_one() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_one"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION global_test_two() returns text
     AS
 'if "global_test" not in SD:
@@ -16,7 +16,7 @@ CREATE FUNCTION global_test_two() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_two"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION static_test() returns int4
     AS
 'if "call" in SD:
@@ -25,7 +25,7 @@ else:
 	SD["call"] = 1
 return SD["call"]
 '
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 SELECT static_test();
  static_test 
 -------------
diff --git a/src/pl/plpython/expected/plpython_import.out b/src/pl/plpython/expected/plpython_import.out
index b59e1821a79..854e989eaf9 100644
--- a/src/pl/plpython/expected/plpython_import.out
+++ b/src/pl/plpython/expected/plpython_import.out
@@ -6,7 +6,7 @@ CREATE FUNCTION import_fail() returns text
 except ImportError:
 	return "failed as expected"
 return "succeeded, that wasn''t supposed to happen"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION import_succeed() returns text
 	AS
 'try:
@@ -25,7 +25,7 @@ except Exception as ex:
 	plpy.notice("import failed -- %s" % str(ex))
 	return "failed, that wasn''t supposed to happen"
 return "succeeded, as expected"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION import_test_one(p text) RETURNS text
 	AS
 'try:
@@ -35,7 +35,7 @@ except ImportError:
     import sha
     digest = sha.new(p)
 return digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION import_test_two(u users) RETURNS text
 	AS
 'plain = u["fname"] + u["lname"]
@@ -46,7 +46,7 @@ except ImportError:
     import sha
     digest = sha.new(plain);
 return "sha hash of " + plain + " is " + digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 -- import python modules
 --
 SELECT import_fail();
diff --git a/src/pl/plpython/expected/plpython_newline.out b/src/pl/plpython/expected/plpython_newline.out
index 27dc2f8ab0c..2bc149257e7 100644
--- a/src/pl/plpython/expected/plpython_newline.out
+++ b/src/pl/plpython/expected/plpython_newline.out
@@ -3,13 +3,13 @@
 --
 CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS
 E'x = 100\ny = 23\nreturn x + y\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS
 E'x = 100\ry = 23\rreturn x + y\r'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS
 E'x = 100\r\ny = 23\r\nreturn x + y\r\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 SELECT newline_lf();
  newline_lf 
 ------------
diff --git a/src/pl/plpython/expected/plpython_params.out b/src/pl/plpython/expected/plpython_params.out
index 46ea7dfb90b..d1a36f36239 100644
--- a/src/pl/plpython/expected/plpython_params.out
+++ b/src/pl/plpython/expected/plpython_params.out
@@ -3,12 +3,12 @@
 --
 CREATE FUNCTION test_param_names0(integer, integer) RETURNS int AS $$
 return args[0] + args[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_param_names1(a0 integer, a1 text) RETURNS boolean AS $$
 assert a0 == args[0]
 assert a1 == args[1]
 return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_param_names2(u users) RETURNS text AS $$
 assert u == args[0]
 if isinstance(u, dict):
@@ -19,7 +19,7 @@ if isinstance(u, dict):
 else:
     s = str(u)
 return s
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- use deliberately wrong parameter names
 CREATE FUNCTION test_param_names3(a0 integer) RETURNS boolean AS $$
 try:
@@ -28,7 +28,7 @@ try:
 except NameError as e:
 	assert e.args[0].find("a1") > -1
 	return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_param_names0(2,7);
  test_param_names0 
 -------------------
diff --git a/src/pl/plpython/expected/plpython_quote.out b/src/pl/plpython/expected/plpython_quote.out
index eed72923aec..1fbe93d5351 100644
--- a/src/pl/plpython/expected/plpython_quote.out
+++ b/src/pl/plpython/expected/plpython_quote.out
@@ -8,7 +8,7 @@ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
         return plpy.quote_ident(t)
     else:
         raise plpy.Error("unrecognized quote type %s" % how)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT quote(t, 'literal') FROM (VALUES
        ('abc'),
        ('a''bc'),
diff --git a/src/pl/plpython/expected/plpython_record.out b/src/pl/plpython/expected/plpython_record.out
index 458330713a8..31de198582b 100644
--- a/src/pl/plpython/expected/plpython_record.out
+++ b/src/pl/plpython/expected/plpython_record.out
@@ -23,7 +23,7 @@ elif typ == 'obj':
 	type_record.first = first
 	type_record.second = second
 	return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
 if retnull:
 	return None
@@ -40,17 +40,17 @@ elif typ == 'obj':
 	return type_record
 elif typ == 'str':
 	return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
 return first + '_in_to_out';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- Test tuple returning functions
 SELECT * FROM test_table_record_as('dict', null, null, false);
  first | second 
@@ -340,7 +340,7 @@ SELECT * FROM test_type_record_as('obj', 'one', 1, false);
 -- errors cases
 CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
     return { 'first': 'first' }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error1();
 ERROR:  key "second" not found in mapping
 HINT:  To return null in a column, add the value None to the mapping with the key named after the column.
@@ -348,7 +348,7 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_record_error1"
 CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
     return [ 'first' ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error2();
 ERROR:  length of returned sequence did not match number of columns in row
 CONTEXT:  while creating return value
@@ -357,7 +357,7 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
     class type_record: pass
     type_record.first = 'first'
     return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error3();
 ERROR:  attribute "second" does not exist in Python object
 HINT:  To return null in a column, let the returned object have an attribute named after column with value None.
@@ -365,7 +365,7 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_record_error3"
 CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
     return 'foo'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error4();
 ERROR:  malformed record literal: "foo"
 DETAIL:  Missing left parenthesis.
diff --git a/src/pl/plpython/expected/plpython_setof.out b/src/pl/plpython/expected/plpython_setof.out
index 170dbc394de..39409400290 100644
--- a/src/pl/plpython/expected/plpython_setof.out
+++ b/src/pl/plpython/expected/plpython_setof.out
@@ -3,20 +3,20 @@
 --
 CREATE FUNCTION test_setof_error() RETURNS SETOF text AS $$
 return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_setof_error();
 ERROR:  returned object cannot be iterated
 DETAIL:  PL/Python set-returning functions must return an iterable object.
 CONTEXT:  PL/Python function "test_setof_error"
 CREATE FUNCTION test_setof_as_list(count integer, content text) RETURNS SETOF text AS $$
 return [ content ]*count
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_as_tuple(count integer, content text) RETURNS SETOF text AS $$
 t = ()
 for i in range(count):
 	t += ( content, )
 return t
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_as_iterator(count integer, content text) RETURNS SETOF text AS $$
 class producer:
 	def __init__ (self, icount, icontent):
@@ -24,13 +24,13 @@ class producer:
 		self.icount = icount
 	def __iter__ (self):
 		return self
-	def next (self):
+	def __next__ (self):
 		if self.icount == 0:
 			raise StopIteration
 		self.icount -= 1
 		return self.icontent
 return producer(count, content)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_spi_in_iterator() RETURNS SETOF text AS
 $$
     for s in ('Hello', 'Brave', 'New', 'World'):
@@ -38,7 +38,7 @@ $$
         yield s
         plpy.execute('select 2')
 $$
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 -- Test set returning functions
 SELECT test_setof_as_list(0, 'list');
  test_setof_as_list 
@@ -130,7 +130,7 @@ global x
 while x <= lim:
     yield x
     x = x + 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT ugly(1, 5);
  ugly 
 ------
@@ -155,7 +155,7 @@ CREATE OR REPLACE FUNCTION get_user_records()
 RETURNS SETOF users
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT get_user_records();
    get_user_records   
 ----------------------
@@ -179,7 +179,7 @@ CREATE OR REPLACE FUNCTION get_user_records2()
 RETURNS TABLE(fname text, lname text, username text, userid int)
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT get_user_records2();
   get_user_records2   
 ----------------------
diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out
index a09df68c7d1..8853e2540d3 100644
--- a/src/pl/plpython/expected/plpython_spi.out
+++ b/src/pl/plpython/expected/plpython_spi.out
@@ -6,17 +6,17 @@ CREATE FUNCTION nested_call_one(a text) RETURNS text
 'q = "SELECT nested_call_two(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 CREATE FUNCTION nested_call_two(a text) RETURNS text
 	AS
 'q = "SELECT nested_call_three(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 CREATE FUNCTION nested_call_three(a text) RETURNS text
 	AS
 'return a'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 -- some spi stuff
 CREATE FUNCTION spi_prepared_plan_test_one(a text) RETURNS text
 	AS
@@ -30,7 +30,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
 	AS
 'if "myplan" not in SD:
@@ -43,7 +43,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
 	AS
 'if "myplan" not in SD:
@@ -57,7 +57,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION join_sequences(s sequences) RETURNS text
 	AS
 'if not s["multipart"]:
@@ -69,7 +69,7 @@ for r in rv:
 	seq = seq + r["sequence"]
 return seq
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
 	AS
 'r = 0
@@ -77,7 +77,7 @@ if a > 1:
     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
 return a + r
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 --
 -- spi and nested calls
 --
@@ -155,7 +155,7 @@ if result.status() > 0:
    return result.nrows()
 else:
    return None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
 INFO:  True
 INFO:  ['foo', 'bar']
@@ -177,7 +177,7 @@ CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return result.nrows()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_nrows_test($$SELECT 1$$);
  result_nrows_test 
 -------------------
@@ -206,7 +206,7 @@ CREATE FUNCTION result_len_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return len(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_len_test($$SELECT 1$$);
  result_len_test 
 -----------------
@@ -246,7 +246,7 @@ result[-1] = {'c': 1000}
 result[:2] = [{'c': 10}, {'c': 100}]
 plpy.info([item['c'] for item in result[:]])
 
-# raises TypeError, but the message differs on Python 2.6, so silence it
+# raises TypeError, catch so further tests could be added
 try:
     plpy.info(result['foo'])
 except TypeError:
@@ -254,7 +254,7 @@ except TypeError:
 else:
     assert False, "TypeError not raised"
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_subscript_test();
 INFO:  2
 INFO:  4
@@ -272,7 +272,7 @@ result = plpy.execute("select 1 where false")
 
 plpy.info(result[:])
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_empty_test();
 INFO:  []
  result_empty_test 
@@ -285,7 +285,7 @@ AS $$
 plan = plpy.prepare(cmd)
 result = plpy.execute(plan)
 return str(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
                       result_str_test                       
 ------------------------------------------------------------
@@ -306,12 +306,12 @@ for row in res:
     if row['lname'] == 'doe':
         does += 1
 return does
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
 res.close()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 assert len(res.fetch(3)) == 3
@@ -329,7 +329,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users order by fname")
 assert len(res.fetch(2)) == 2
@@ -342,7 +342,7 @@ except AttributeError:
 assert item['fname'] == 'rick'
 
 assert len(res.fetch(2)) == 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
@@ -352,7 +352,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION next_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
@@ -365,7 +365,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users where false")
 assert len(res.fetch(1)) == 0
@@ -378,7 +378,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
 plan = plpy.prepare(
     "select fname, lname from users where fname like $1 || '%' order by fname",
@@ -387,12 +387,12 @@ for row in plpy.cursor(plan, ["w"]):
     yield row['fname']
 for row in plan.cursor(["j"]):
     yield row['fname']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
                     ["text"])
 c = plpy.cursor(plan, ["a", "b"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TYPE test_composite_type AS (
   a1 int,
   a2 varchar
@@ -401,7 +401,7 @@ CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS
 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
 return res[0]["c1"]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT simple_cursor_test();
  simple_cursor_test 
 --------------------
diff --git a/src/pl/plpython/expected/plpython_subtransaction.out b/src/pl/plpython/expected/plpython_subtransaction.out
index 2a56541917d..43d9277a33b 100644
--- a/src/pl/plpython/expected/plpython_subtransaction.out
+++ b/src/pl/plpython/expected/plpython_subtransaction.out
@@ -14,7 +14,7 @@ with plpy.subtransaction():
         plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
     elif what_error == "Python":
         raise Exception("Python exception")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_ctx_test();
  subtransaction_ctx_test 
 -------------------------
@@ -71,7 +71,7 @@ with plpy.subtransaction():
             raise
         plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_nested_test();
 ERROR:  spiexceptions.SyntaxError: syntax error at or near "error"
 LINE 1: error
@@ -111,7 +111,7 @@ with plpy.subtransaction():
     plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
     plpy.execute("SELECT subtransaction_nested_test('t')")
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_deeply_nested_test();
 NOTICE:  Swallowed SyntaxError('syntax error at or near "error"')
  subtransaction_deeply_nested_test 
@@ -133,42 +133,42 @@ TRUNCATE subtransaction_tbl;
 CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
 AS $$
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__exit__(None, None, None)
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
 AS $$
 s = plpy.subtransaction()
 s.__enter__()
 s.__exit__(None, None, None)
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
 AS $$
 s = plpy.subtransaction()
 s.__enter__()
 s.__enter__()
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- No warnings here, as the subtransaction gets indeed closed
 CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
 AS $$
 with plpy.subtransaction() as s:
     s.__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
 AS $$
 try:
@@ -176,7 +176,7 @@ try:
         s.__exit__(None, None, None)
 except ValueError as e:
     raise ValueError(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_exit_without_enter();
 ERROR:  ValueError: this subtransaction has not been entered
 CONTEXT:  Traceback (most recent call last):
@@ -255,7 +255,7 @@ try:
     plpy.execute(p, ["wrong"])
 except plpy.SPIError:
     plpy.warning("Caught a SPI error")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_mix_explicit_and_implicit();
 WARNING:  Caught a SPI error from an explicit subtransaction
 WARNING:  Caught a SPI error
@@ -278,7 +278,7 @@ AS $$
 s = plpy.subtransaction()
 s.enter()
 s.exit(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_alternative_names();
  subtransaction_alternative_names 
 ----------------------------------
@@ -294,7 +294,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT try_catch_inside_subtransaction();
 NOTICE:  caught
  try_catch_inside_subtransaction 
@@ -318,7 +318,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT pk_violation_inside_subtransaction();
 NOTICE:  caught
  pk_violation_inside_subtransaction 
@@ -340,7 +340,7 @@ with plpy.subtransaction():
     cur.fetch(10)
 fetched = cur.fetch(10);
 return int(fetched[5]["i"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
 try:
     with plpy.subtransaction():
@@ -351,7 +351,7 @@ except plpy.SPIError:
     fetched = cur.fetch(10)
     return int(fetched[5]["i"])
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
 try:
     with plpy.subtransaction():
@@ -364,7 +364,7 @@ except plpy.SPIError:
     fetched = cur.fetch(5)
     return fetched[2]["i"]
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
 try:
     with plpy.subtransaction():
@@ -374,7 +374,7 @@ except plpy.SPIError:
     cur.close()
     return True
 return False # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT cursor_in_subxact();
  cursor_in_subxact 
 -------------------
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index 39b994f4468..13c14119c08 100644
--- a/src/pl/plpython/expected/plpython_test.out
+++ b/src/pl/plpython/expected/plpython_test.out
@@ -1,7 +1,7 @@
 -- first some tests of basic functionality
-CREATE EXTENSION plpython2u;
+CREATE EXTENSION plpython3u;
 -- really stupid function just to get the module loaded
-CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
+CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 select stupid();
  stupid 
 --------
@@ -9,7 +9,7 @@ select stupid();
 (1 row)
 
 -- check 2/3 versioning
-CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
+CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 select stupidn();
  stupidn 
 ---------
@@ -26,7 +26,7 @@ for key in keys:
     out.append("%s: %s" % (key, u[key]))
 words = a1 + " " + a2 + " => {" + ", ".join(out) + "}"
 return words'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 select "Argument test #1"(users, fname, lname) from users where lname = 'doe' order by 1;
                            Argument test #1                            
 -----------------------------------------------------------------------
@@ -41,7 +41,7 @@ $$
 contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
 contents.sort()
 return contents
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select module_contents();
  module_contents 
 -----------------
@@ -78,7 +78,7 @@ plpy.info('info', 37, [1, 2, 3])
 plpy.notice('notice')
 plpy.warning('warning')
 plpy.error('error')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT elog_test_basic();
 INFO:  info
 INFO:  37
diff --git a/src/pl/plpython/expected/plpython_transaction.out b/src/pl/plpython/expected/plpython_transaction.out
index 72d1e45a768..659ccefc797 100644
--- a/src/pl/plpython/expected/plpython_transaction.out
+++ b/src/pl/plpython/expected/plpython_transaction.out
@@ -1,6 +1,6 @@
 CREATE TABLE test1 (a int, b text);
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -22,7 +22,7 @@ SELECT * FROM test1;
 
 TRUNCATE test1;
 DO
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -44,7 +44,7 @@ SELECT * FROM test1;
 TRUNCATE test1;
 -- not allowed in a function
 CREATE FUNCTION transaction_test2() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -67,7 +67,7 @@ SELECT * FROM test1;
 
 -- also not allowed if procedure is called from a function
 CREATE FUNCTION transaction_test3() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("CALL transaction_test1()")
 return 1
@@ -85,19 +85,19 @@ SELECT * FROM test1;
 
 -- DO block inside function
 CREATE FUNCTION transaction_test4() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 return 1
 $$;
 SELECT transaction_test4();
 ERROR:  spiexceptions.InvalidTransactionTermination: spiexceptions.InvalidTransactionTermination: invalid transaction termination
 CONTEXT:  Traceback (most recent call last):
   PL/Python function "transaction_test4", line 2, in <module>
-    plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+    plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 PL/Python function "transaction_test4"
 -- commit inside subtransaction (prohibited)
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 s = plpy.subtransaction()
 s.enter()
 plpy.commit()
@@ -112,7 +112,7 @@ PL/Python anonymous code block
 CREATE TABLE test2 (x int);
 INSERT INTO test2 VALUES (0), (1), (2), (3), (4);
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.commit()
@@ -135,7 +135,7 @@ SELECT * FROM pg_cursors;
 
 -- error in cursor loop with commit
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x'])
     plpy.commit()
@@ -159,7 +159,7 @@ SELECT * FROM pg_cursors;
 
 -- rollback inside cursor loop
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.rollback()
@@ -176,7 +176,7 @@ SELECT * FROM pg_cursors;
 
 -- first commit then rollback inside cursor loop
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     if row['x'] % 2 == 0:
@@ -200,7 +200,7 @@ SELECT * FROM pg_cursors;
 -- check handling of an error during COMMIT
 CREATE TABLE testpk (id int PRIMARY KEY);
 CREATE TABLE testfk(f1 int REFERENCES testpk DEFERRABLE INITIALLY DEFERRED);
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 # this insert will fail during commit:
 plpy.execute("INSERT INTO testfk VALUES (0)")
 plpy.commit()
@@ -222,7 +222,7 @@ SELECT * FROM testfk;
 ----
 (0 rows)
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 # this insert will fail during commit:
 plpy.execute("INSERT INTO testfk VALUES (0)")
 try:
diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out
index 742988a5b59..dd1ca32fa49 100644
--- a/src/pl/plpython/expected/plpython_trigger.out
+++ b/src/pl/plpython/expected/plpython_trigger.out
@@ -15,20 +15,20 @@ if TD["new"]["fname"] == "william":
 	TD["new"]["fname"] = TD["args"][0]
 	rv = "MODIFY"
 return rv'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION users_update() returns trigger
 	AS
 'if TD["event"] == "UPDATE":
 	if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
 		return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION users_delete() RETURNS trigger
 	AS
 'if TD["old"]["fname"] == TD["args"][0]:
 	return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
 	EXECUTE PROCEDURE users_insert ('willem');
 CREATE TRIGGER users_update_trig BEFORE UPDATE ON users FOR EACH ROW
@@ -71,7 +71,7 @@ CREATE TABLE trigger_test_generated (
 	i int,
         j int GENERATED ALWAYS AS (i * 2) STORED
 );
-CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
 
 if 'relid' in TD:
 	TD['relid'] = "bogus:12345"
@@ -328,7 +328,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid1() RETURNS trigger
 AS $$
     return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger1
 BEFORE INSERT ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid1();
@@ -341,7 +341,7 @@ DROP TRIGGER stupid_trigger1 ON trigger_test;
 CREATE FUNCTION stupid2() RETURNS trigger
 AS $$
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger2
 BEFORE DELETE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid2();
@@ -353,7 +353,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid3() RETURNS trigger
 AS $$
     return "foo"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid3();
@@ -365,8 +365,8 @@ DROP TRIGGER stupid_trigger3 ON trigger_test;
 -- Unicode variant
 CREATE FUNCTION stupid3u() RETURNS trigger
 AS $$
-    return u"foo"
-$$ LANGUAGE plpythonu;
+    return "foo"
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid3u();
@@ -380,7 +380,7 @@ CREATE FUNCTION stupid4() RETURNS trigger
 AS $$
     del TD["new"]
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger4
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid4();
@@ -394,7 +394,7 @@ CREATE FUNCTION stupid5() RETURNS trigger
 AS $$
     TD["new"] = ['foo', 'bar']
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger5
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid5();
@@ -408,7 +408,7 @@ CREATE FUNCTION stupid6() RETURNS trigger
 AS $$
     TD["new"] = {1: 'foo', 2: 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger6
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid6();
@@ -422,7 +422,7 @@ CREATE FUNCTION stupid7() RETURNS trigger
 AS $$
     TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid7();
@@ -434,9 +434,9 @@ DROP TRIGGER stupid_trigger7 ON trigger_test;
 -- Unicode variant
 CREATE FUNCTION stupid7u() RETURNS trigger
 AS $$
-    TD["new"] = {u'v': 'foo', u'a': 'bar'}
+    TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid7u();
@@ -461,7 +461,7 @@ CREATE FUNCTION test_null() RETURNS trigger
 AS $$
     TD["new"]['v'] = None
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER test_null_trigger
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE test_null();
@@ -481,7 +481,7 @@ SET DateStyle = 'ISO';
 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
 CREATE TRIGGER set_modif_time BEFORE UPDATE ON pb
   FOR EACH ROW EXECUTE PROCEDURE set_modif_time();
@@ -507,7 +507,7 @@ CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
     TD['new']['f1'] = (3, False)
     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
 INSERT INTO composite_trigger_test VALUES (NULL, NULL);
@@ -521,7 +521,7 @@ SELECT * FROM composite_trigger_test;
 CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
 INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL);
@@ -540,7 +540,7 @@ CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer);
 CREATE TABLE composite_trigger_nested_test(c comp3);
 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
 INSERT INTO composite_trigger_nested_test VALUES (NULL);
@@ -555,7 +555,7 @@ SELECT * FROM composite_trigger_nested_test;
 (3 rows)
 
 -- check that using a function as a trigger over two tables works correctly
-CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
     TD["new"]["data"] = '1234'
     return 'MODIFY'
 $$;
@@ -581,7 +581,7 @@ SELECT * FROM b;
 -- check that SQL run in trigger code can see transition tables
 CREATE TABLE transition_table_test (id int, name text);
 INSERT INTO transition_table_test VALUES (1, 'a');
-CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
 $$
     rv = plpy.execute("SELECT * FROM old_table")
     assert(rv.nrows() == 1)
@@ -601,7 +601,7 @@ DROP TABLE transition_table_test;
 DROP FUNCTION transition_table_test_f();
 -- dealing with generated columns
 CREATE FUNCTION generated_test_func1() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 TD['new']['j'] = 5  # not allowed
 return 'MODIFY'
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index 0a2659fe292..a470911c2ec 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -7,23 +7,23 @@
 CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bool(true);
-INFO:  (True, <type 'bool'>)
+INFO:  (True, <class 'bool'>)
  test_type_conversion_bool 
 ---------------------------
  t
 (1 row)
 
 SELECT * FROM test_type_conversion_bool(false);
-INFO:  (False, <type 'bool'>)
+INFO:  (False, <class 'bool'>)
  test_type_conversion_bool 
 ---------------------------
  f
 (1 row)
 
 SELECT * FROM test_type_conversion_bool(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_bool 
 ---------------------------
  
@@ -48,7 +48,7 @@ elif n == 5:
    ret = [0]
 plpy.info(ret, not not ret)
 return ret
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bool_other(0);
 INFO:  (0, False)
  test_type_conversion_bool_other 
@@ -94,16 +94,16 @@ INFO:  ([0], True)
 CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_char('a');
-INFO:  ('a', <type 'str'>)
+INFO:  ('a', <class 'str'>)
  test_type_conversion_char 
 ---------------------------
  a
 (1 row)
 
 SELECT * FROM test_type_conversion_char(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_char 
 ---------------------------
  
@@ -112,23 +112,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int2(100::int2);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int2 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int2(-100::int2);
-INFO:  (-100, <type 'int'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int2 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int2(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int2 
 ---------------------------
                           
@@ -137,23 +137,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int4(100);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int4 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int4(-100);
-INFO:  (-100, <type 'int'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int4 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int4(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int4 
 ---------------------------
                           
@@ -162,30 +162,30 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int8(100);
-INFO:  (100L, <type 'long'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(-100);
-INFO:  (-100L, <type 'long'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(5000000000);
-INFO:  (5000000000L, <type 'long'>)
+INFO:  (5000000000, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                 5000000000
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int8 
 ---------------------------
                           
@@ -196,7 +196,7 @@ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
 # between decimal and cdecimal
 plpy.info(str(x), x.__class__.__name__)
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_numeric(100);
 INFO:  ('100', 'Decimal')
  test_type_conversion_numeric 
@@ -256,30 +256,30 @@ INFO:  ('None', 'NoneType')
 CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_float4(100);
-INFO:  (100.0, <type 'float'>)
+INFO:  (100.0, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                          100
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(-100);
-INFO:  (-100.0, <type 'float'>)
+INFO:  (-100.0, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                         -100
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(5000.5);
-INFO:  (5000.5, <type 'float'>)
+INFO:  (5000.5, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                       5000.5
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_float4 
 -----------------------------
                             
@@ -288,37 +288,37 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_float8(100);
-INFO:  (100.0, <type 'float'>)
+INFO:  (100.0, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                          100
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(-100);
-INFO:  (-100.0, <type 'float'>)
+INFO:  (-100.0, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                         -100
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(5000000000.5);
-INFO:  (5000000000.5, <type 'float'>)
+INFO:  (5000000000.5, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                 5000000000.5
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_float8 
 -----------------------------
                             
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(100100100.654321);
-INFO:  (100100100.654321, <type 'float'>)
+INFO:  (100100100.654321, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
             100100100.654321
@@ -327,23 +327,23 @@ INFO:  (100100100.654321, <type 'float'>)
 CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_oid(100);
-INFO:  (100L, <type 'long'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_oid 
 --------------------------
                       100
 (1 row)
 
 SELECT * FROM test_type_conversion_oid(2147483649);
-INFO:  (2147483649L, <type 'long'>)
+INFO:  (2147483649, <class 'int'>)
  test_type_conversion_oid 
 --------------------------
                2147483649
 (1 row)
 
 SELECT * FROM test_type_conversion_oid(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_oid 
 --------------------------
                          
@@ -352,16 +352,16 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_text('hello world');
-INFO:  ('hello world', <type 'str'>)
+INFO:  ('hello world', <class 'str'>)
  test_type_conversion_text 
 ---------------------------
  hello world
 (1 row)
 
 SELECT * FROM test_type_conversion_text(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_text 
 ---------------------------
  
@@ -370,23 +370,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bytea('hello world');
-INFO:  ('hello world', <type 'str'>)
+INFO:  (b'hello world', <class 'bytes'>)
  test_type_conversion_bytea 
 ----------------------------
  \x68656c6c6f20776f726c64
 (1 row)
 
 SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
-INFO:  ('null\x00byte', <type 'str'>)
+INFO:  (b'null\x00byte', <class 'bytes'>)
  test_type_conversion_bytea 
 ----------------------------
  \x6e756c6c0062797465
 (1 row)
 
 SELECT * FROM test_type_conversion_bytea(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_bytea 
 ----------------------------
  
@@ -395,14 +395,14 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
 import marshal
 return marshal.dumps('hello world')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
 import marshal
 try:
     return marshal.loads(x)
 except ValueError as e:
     return 'FAILED: ' + str(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
  test_type_unmarshal 
 ---------------------
@@ -415,7 +415,7 @@ SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
 CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
 CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_booltrue(true, true);
  test_type_conversion_booltrue 
 -------------------------------
@@ -432,21 +432,21 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
 CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_uint2 
 ----------------------------
                          50
 (1 row)
 
 SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
 ERROR:  value for domain uint2 violates check constraint "uint2_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_uint2"
 SELECT * FROM test_type_conversion_uint2(null, 1);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_uint2 
 ----------------------------
                           1
@@ -455,7 +455,7 @@ INFO:  (None, <type 'NoneType'>)
 CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
 CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_nnint(10, 20);
  test_type_conversion_nnint 
 ----------------------------
@@ -472,9 +472,9 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
 CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
-INFO:  ('hello wold', <type 'str'>)
+INFO:  (b'hello wold', <class 'bytes'>)
  test_type_conversion_bytea10 
 ------------------------------
  \x68656c6c6f20776f6c64
@@ -483,14 +483,14 @@ INFO:  ('hello wold', <type 'str'>)
 SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
-INFO:  ('hello word', <type 'str'>)
+INFO:  (b'hello word', <class 'bytes'>)
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_bytea10"
 SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 SELECT * FROM test_type_conversion_bytea10('hello word', null);
-INFO:  ('hello word', <type 'str'>)
+INFO:  (b'hello word', <class 'bytes'>)
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_bytea10"
@@ -500,58 +500,58 @@ PL/Python function "test_type_conversion_bytea10"
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
-INFO:  ([0, 100], <type 'list'>)
+INFO:  ([0, 100], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {0,100}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
-INFO:  ([0, -100, 55], <type 'list'>)
+INFO:  ([0, -100, 55], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {0,-100,55}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
-INFO:  ([None, 1], <type 'list'>)
+INFO:  ([None, 1], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {NULL,1}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
-INFO:  ([], <type 'list'>)
+INFO:  ([], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(NULL);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_array_int4 
 ---------------------------------
  
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-INFO:  ([[1, 2, 3], [4, 5, 6]], <type 'list'>)
+INFO:  ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {{1,2,3},{4,5,6}}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <type 'list'>)
+INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
           test_type_conversion_array_int4          
 ---------------------------------------------------
  {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
-INFO:  ([1, 2, 3], <type 'list'>)
+INFO:  ([1, 2, 3], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {1,2,3}
@@ -560,9 +560,9 @@ INFO:  ([1, 2, 3], <type 'list'>)
 CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
-INFO:  ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <type 'list'>)
+INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
           test_type_conversion_array_int8          
 ---------------------------------------------------
  {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
@@ -571,10 +571,10 @@ INFO:  ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <
 CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
             [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
-INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <type 'list'>)
+INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
                                                  test_type_conversion_array_date                                                 
 ---------------------------------------------------------------------------------------------------------------------------------
  {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
@@ -583,12 +583,12 @@ INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']
 CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
             [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
             [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
             ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
-INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <type 'list'>)
+INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
                                                                                                                                                       test_type_conversion_array_timestamp                                                                                                                                                      
 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
@@ -598,9 +598,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemint4(8,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
                                                                                                                                                                                                                                                                              pyreturnmultidemint4                                                                                                                                                                                                                                                                              
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
@@ -610,9 +610,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemint8(5,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
                                                                                                                                                                                    pyreturnmultidemint8                                                                                                                                                                                    
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
@@ -622,9 +622,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemfloat4(6,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
                                                                                                                                                                                                                 pyreturnmultidemfloat4                                                                                                                                                                                                                 
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
@@ -634,9 +634,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemfloat8(7,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
                                                                                                                                                                                                                                               pyreturnmultidemfloat8                                                                                                                                                                                                                                               
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
@@ -645,16 +645,16 @@ INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6],
 CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
-INFO:  (['foo', 'bar'], <type 'list'>)
+INFO:  (['foo', 'bar'], <class 'list'>)
  test_type_conversion_array_text 
 ---------------------------------
  {foo,bar}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
-INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
+INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
  test_type_conversion_array_text 
 ---------------------------------
  {{foo,bar},{foo2,bar2}}
@@ -663,9 +663,9 @@ INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
 CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
-INFO:  (['\xde\xad\xbe\xef', None], <type 'list'>)
+INFO:  ([b'\xde\xad\xbe\xef', None], <class 'list'>)
  test_type_conversion_array_bytea 
 ----------------------------------
  {"\\xdeadbeef",NULL}
@@ -673,7 +673,7 @@ INFO:  (['\xde\xad\xbe\xef', None], <type 'list'>)
 
 CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_mixed1();
  test_type_conversion_array_mixed1 
 -----------------------------------
@@ -682,14 +682,14 @@ SELECT * FROM test_type_conversion_array_mixed1();
 
 CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_mixed2();
 ERROR:  invalid input syntax for type integer: "abc"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_array_mixed2"
 CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
 return [[1,2,3],[4,5]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_mdarray_malformed();
 ERROR:  wrong length of inner sequence: has length 2, but 3 was expected
 DETAIL:  To construct a multidimensional array, the inner sequences must all have the same length.
@@ -697,14 +697,14 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_mdarray_malformed"
 CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
 return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_mdarray_toodeep();
 ERROR:  number of array dimensions exceeds the maximum allowed (6)
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_mdarray_toodeep"
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_record();
  test_type_conversion_array_record 
 -----------------------------------
@@ -713,7 +713,7 @@ SELECT * FROM test_type_conversion_array_record();
 
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_string();
  test_type_conversion_array_string 
 -----------------------------------
@@ -722,7 +722,7 @@ SELECT * FROM test_type_conversion_array_string();
 
 CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
 return ('abc', 'def')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_tuple();
  test_type_conversion_array_tuple 
 ----------------------------------
@@ -731,7 +731,7 @@ SELECT * FROM test_type_conversion_array_tuple();
 
 CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
 return 5
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_error();
 ERROR:  return value of function with array return type is not a Python sequence
 CONTEXT:  while creating return value
@@ -743,16 +743,16 @@ CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AN
 CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
-INFO:  ([0, 100], <type 'list'>)
+INFO:  ([0, 100], <class 'list'>)
  test_type_conversion_array_domain 
 -----------------------------------
  {0,100}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_array_domain 
 -----------------------------------
  
@@ -760,7 +760,7 @@ INFO:  (None, <type 'NoneType'>)
 
 CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
 return [2,1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain_check_violation();
 ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
 CONTEXT:  while creating return value
@@ -771,9 +771,9 @@ PL/Python function "test_type_conversion_array_domain_check_violation"
 CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_read_uint2_array(array[1::uint2]);
-INFO:  ([1], <type 'list'>)
+INFO:  ([1], <class 'list'>)
  test_read_uint2_array 
 -----------------------
                      1
@@ -781,7 +781,7 @@ INFO:  ([1], <type 'list'>)
 
 CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_build_uint2_array(1::int2);
  test_build_uint2_array 
 ------------------------
@@ -800,7 +800,7 @@ PL/Python function "test_build_uint2_array"
 CREATE FUNCTION test_type_conversion_domain_array(x integer[])
   RETURNS ordered_pair_domain[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_domain_array(array[2,4]);
 ERROR:  return value of function with array return type is not a Python sequence
 CONTEXT:  while creating return value
@@ -813,9 +813,9 @@ CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
   RETURNS integer AS $$
 plpy.info(x, type(x))
 return x[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_domain_array2(array[2,4]);
-INFO:  ([2, 4], <type 'list'>)
+INFO:  ([2, 4], <class 'list'>)
  test_type_conversion_domain_array2 
 ------------------------------------
                                   4
@@ -827,9 +827,9 @@ CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
   RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
-INFO:  ([[2, 4]], <type 'list'>)
+INFO:  ([[2, 4]], <class 'list'>)
  test_type_conversion_array_domain_array 
 -----------------------------------------
  {2,4}
@@ -846,7 +846,7 @@ CREATE TABLE employee (
 INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
 CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
 return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT name, test_composite_table_input(employee.*) FROM employee;
  name | test_composite_table_input 
 ------+----------------------------
@@ -876,7 +876,7 @@ CREATE TYPE named_pair AS (
 );
 CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
 return sum(p.values())
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_composite_type_input(row(1, 2));
  test_composite_type_input 
 ---------------------------
@@ -896,7 +896,7 @@ SELECT test_composite_type_input(row(1, 2));
 CREATE TYPE nnint_container AS (f1 int, f2 nnint);
 CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
 return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT nnint_test(null, 3);
  nnint_test 
 ------------
@@ -913,7 +913,7 @@ PL/Python function "nnint_test"
 CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
 CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
 return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT read_ordered_named_pair(row(1, 2));
  read_ordered_named_pair 
 -------------------------
@@ -924,7 +924,7 @@ SELECT read_ordered_named_pair(row(2, 1));  -- fail
 ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
 CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
 return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT build_ordered_named_pair(1,2);
  build_ordered_named_pair 
 --------------------------
@@ -937,7 +937,7 @@ CONTEXT:  while creating return value
 PL/Python function "build_ordered_named_pair"
 CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
 return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT build_ordered_named_pairs(1,2);
  build_ordered_named_pairs 
 ---------------------------
@@ -952,7 +952,7 @@ PL/Python function "build_ordered_named_pairs"
 -- Prepared statements
 --
 CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
 rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
@@ -965,7 +965,7 @@ SELECT test_prep_bool_input(); -- 1
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
 rv = plpy.execute(plan, [0], 5)
@@ -980,7 +980,7 @@ INFO:  {'val': False}
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
 rv = plpy.execute(plan, [bb], 5)
@@ -993,7 +993,7 @@ SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
 rv = plpy.execute(plan, [], 5)
@@ -1001,7 +1001,7 @@ plpy.info(rv[0])
 return rv[0]['val']
 $$;
 SELECT test_prep_bytea_output();
-INFO:  {'val': '\xaa\x00\xbb'}
+INFO:  {'val': b'\xaa\x00\xbb'}
  test_prep_bytea_output 
 ------------------------
  \xaa00bb
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
deleted file mode 100644
index a6ec10d5e18..00000000000
--- a/src/pl/plpython/expected/plpython_types_3.out
+++ /dev/null
@@ -1,1009 +0,0 @@
---
--- Test data type behavior
---
---
--- Base/common types
---
-CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bool(true);
-INFO:  (True, <class 'bool'>)
- test_type_conversion_bool 
----------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool(false);
-INFO:  (False, <class 'bool'>)
- test_type_conversion_bool 
----------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_bool 
----------------------------
- 
-(1 row)
-
--- test various other ways to express Booleans in Python
-CREATE FUNCTION test_type_conversion_bool_other(n int) RETURNS bool AS $$
-# numbers
-if n == 0:
-   ret = 0
-elif n == 1:
-   ret = 5
-# strings
-elif n == 2:
-   ret = ''
-elif n == 3:
-   ret = 'fa' # true in Python, false in PostgreSQL
-# containers
-elif n == 4:
-   ret = []
-elif n == 5:
-   ret = [0]
-plpy.info(ret, not not ret)
-return ret
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bool_other(0);
-INFO:  (0, False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(1);
-INFO:  (5, True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(2);
-INFO:  ('', False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(3);
-INFO:  ('fa', True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(4);
-INFO:  ([], False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(5);
-INFO:  ([0], True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_char('a');
-INFO:  ('a', <class 'str'>)
- test_type_conversion_char 
----------------------------
- a
-(1 row)
-
-SELECT * FROM test_type_conversion_char(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_char 
----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int2(100::int2);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int2 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int2(-100::int2);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int2 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int2(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int2 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int4(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int4 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int4(-100);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int4 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int4(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int4 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int8(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(-100);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(5000000000);
-INFO:  (5000000000, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                5000000000
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int8 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
-# print just the class name, not the type, to avoid differences
-# between decimal and cdecimal
-plpy.info(str(x), x.__class__.__name__)
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_numeric(100);
-INFO:  ('100', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                          100
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(-100);
-INFO:  ('-100', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                         -100
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(100.0);
-INFO:  ('100.0', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                        100.0
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(100.00);
-INFO:  ('100.00', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                       100.00
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(5000000000.5);
-INFO:  ('5000000000.5', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                 5000000000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(1234567890.0987654321);
-INFO:  ('1234567890.0987654321', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-        1234567890.0987654321
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(-1234567890.0987654321);
-INFO:  ('-1234567890.0987654321', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-       -1234567890.0987654321
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(null);
-INFO:  ('None', 'NoneType')
- test_type_conversion_numeric 
-------------------------------
-                             
-(1 row)
-
-CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_float4(100);
-INFO:  (100.0, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                         100
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(-100);
-INFO:  (-100.0, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                        -100
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(5000.5);
-INFO:  (5000.5, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                      5000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_float4 
------------------------------
-                            
-(1 row)
-
-CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_float8(100);
-INFO:  (100.0, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                         100
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(-100);
-INFO:  (-100.0, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                        -100
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(5000000000.5);
-INFO:  (5000000000.5, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                5000000000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_float8 
------------------------------
-                            
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(100100100.654321);
-INFO:  (100100100.654321, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-            100100100.654321
-(1 row)
-
-CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_oid(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_oid 
---------------------------
-                      100
-(1 row)
-
-SELECT * FROM test_type_conversion_oid(2147483649);
-INFO:  (2147483649, <class 'int'>)
- test_type_conversion_oid 
---------------------------
-               2147483649
-(1 row)
-
-SELECT * FROM test_type_conversion_oid(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_oid 
---------------------------
-                         
-(1 row)
-
-CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_text('hello world');
-INFO:  ('hello world', <class 'str'>)
- test_type_conversion_text 
----------------------------
- hello world
-(1 row)
-
-SELECT * FROM test_type_conversion_text(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_text 
----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bytea('hello world');
-INFO:  (b'hello world', <class 'bytes'>)
- test_type_conversion_bytea 
-----------------------------
- \x68656c6c6f20776f726c64
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
-INFO:  (b'null\x00byte', <class 'bytes'>)
- test_type_conversion_bytea 
-----------------------------
- \x6e756c6c0062797465
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_bytea 
-----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
-import marshal
-return marshal.dumps('hello world')
-$$ LANGUAGE plpython3u;
-CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
-import marshal
-try:
-    return marshal.loads(x)
-except ValueError as e:
-    return 'FAILED: ' + str(e)
-$$ LANGUAGE plpython3u;
-SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
- test_type_unmarshal 
----------------------
- hello world
-(1 row)
-
---
--- Domains
---
-CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
-CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_booltrue(true, true);
- test_type_conversion_booltrue 
--------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_booltrue(false, true);
-ERROR:  value for domain booltrue violates check constraint "booltrue_check"
-SELECT * FROM test_type_conversion_booltrue(true, false);
-ERROR:  value for domain booltrue violates check constraint "booltrue_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_booltrue"
-CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
-CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
-plpy.info(x, type(x))
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
-INFO:  (100, <class 'int'>)
- test_type_conversion_uint2 
-----------------------------
-                         50
-(1 row)
-
-SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
-INFO:  (100, <class 'int'>)
-ERROR:  value for domain uint2 violates check constraint "uint2_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_uint2"
-SELECT * FROM test_type_conversion_uint2(null, 1);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_uint2 
-----------------------------
-                          1
-(1 row)
-
-CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
-CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_nnint(10, 20);
- test_type_conversion_nnint 
-----------------------------
-                         20
-(1 row)
-
-SELECT * FROM test_type_conversion_nnint(null, 20);
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-SELECT * FROM test_type_conversion_nnint(10, null);
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_nnint"
-CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL);
-CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
-plpy.info(x, type(x))
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
-INFO:  (b'hello wold', <class 'bytes'>)
- test_type_conversion_bytea10 
-------------------------------
- \x68656c6c6f20776f6c64
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
-INFO:  (b'hello word', <class 'bytes'>)
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_bytea10"
-SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-SELECT * FROM test_type_conversion_bytea10('hello word', null);
-INFO:  (b'hello word', <class 'bytes'>)
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_bytea10"
---
--- Arrays
---
-CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
-INFO:  ([0, 100], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {0,100}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
-INFO:  ([0, -100, 55], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {0,-100,55}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
-INFO:  ([None, 1], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {NULL,1}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
-INFO:  ([], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(NULL);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_array_int4 
----------------------------------
- 
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-INFO:  ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {{1,2,3},{4,5,6}}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
-          test_type_conversion_array_int4          
----------------------------------------------------
- {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
-INFO:  ([1, 2, 3], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {1,2,3}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
-          test_type_conversion_array_int8          
----------------------------------------------------
- {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
-            [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
-INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
-                                                 test_type_conversion_array_date                                                 
----------------------------------------------------------------------------------------------------------------------------------
- {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
-            [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
-            [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
-            ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
-INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
-                                                                                                                                                      test_type_conversion_array_timestamp                                                                                                                                                      
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 ) RETURNS int4[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemint4(8,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
-                                                                                                                                                                                                                                                                             pyreturnmultidemint4                                                                                                                                                                                                                                                                              
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 ) RETURNS int8[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemint8(5,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
-                                                                                                                                                                                   pyreturnmultidemint8                                                                                                                                                                                    
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4 ) RETURNS float4[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemfloat4(6,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
-                                                                                                                                                                                                                pyreturnmultidemfloat4                                                                                                                                                                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4 ) RETURNS float8[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemfloat8(7,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
-                                                                                                                                                                                                                                              pyreturnmultidemfloat8                                                                                                                                                                                                                                               
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
-INFO:  (['foo', 'bar'], <class 'list'>)
- test_type_conversion_array_text 
----------------------------------
- {foo,bar}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
-INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
- test_type_conversion_array_text 
----------------------------------
- {{foo,bar},{foo2,bar2}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
-INFO:  ([b'\xde\xad\xbe\xef', None], <class 'list'>)
- test_type_conversion_array_bytea 
-----------------------------------
- {"\\xdeadbeef",NULL}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
-return [123, 'abc']
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_mixed1();
- test_type_conversion_array_mixed1 
------------------------------------
- {123,abc}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
-return [123, 'abc']
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_mixed2();
-ERROR:  invalid input syntax for type integer: "abc"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_mixed2"
-CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
-return [[1,2,3],[4,5]]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_mdarray_malformed();
-ERROR:  wrong length of inner sequence: has length 2, but 3 was expected
-DETAIL:  To construct a multidimensional array, the inner sequences must all have the same length.
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_mdarray_malformed"
-CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
-return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_mdarray_toodeep();
-ERROR:  number of array dimensions exceeds the maximum allowed (6)
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_mdarray_toodeep"
-CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
-return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_record();
- test_type_conversion_array_record 
------------------------------------
- {"(one,42)","(two,11)"}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
-return 'abc'
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_string();
- test_type_conversion_array_string 
------------------------------------
- {a,b,c}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
-return ('abc', 'def')
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_tuple();
- test_type_conversion_array_tuple 
-----------------------------------
- {abc,def}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
-return 5
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_error();
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_error"
---
--- Domains over arrays
---
-CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AND VALUE[1] < VALUE[2]);
-CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
-INFO:  ([0, 100], <class 'list'>)
- test_type_conversion_array_domain 
------------------------------------
- {0,100}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_array_domain 
------------------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
-return [2,1]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_domain_check_violation();
-ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_domain_check_violation"
---
--- Arrays of domains
---
-CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
-plpy.info(x, type(x))
-return x[0]
-$$ LANGUAGE plpythonu;
-select test_read_uint2_array(array[1::uint2]);
-INFO:  ([1], <class 'list'>)
- test_read_uint2_array 
------------------------
-                     1
-(1 row)
-
-CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
-return [x, x]
-$$ LANGUAGE plpythonu;
-select test_build_uint2_array(1::int2);
- test_build_uint2_array 
-------------------------
- {1,1}
-(1 row)
-
-select test_build_uint2_array(-1::int2);  -- fail
-ERROR:  value for domain uint2 violates check constraint "uint2_check"
-CONTEXT:  while creating return value
-PL/Python function "test_build_uint2_array"
---
--- ideally this would work, but for now it doesn't, because the return value
--- is [[2,4], [2,4]] which our conversion code thinks should become a 2-D
--- integer array, not an array of arrays.
---
-CREATE FUNCTION test_type_conversion_domain_array(x integer[])
-  RETURNS ordered_pair_domain[] AS $$
-return [x, x]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_domain_array(array[2,4]);
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_domain_array"
-select test_type_conversion_domain_array(array[4,2]);  -- fail
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_domain_array"
-CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
-  RETURNS integer AS $$
-plpy.info(x, type(x))
-return x[1]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_domain_array2(array[2,4]);
-INFO:  ([2, 4], <class 'list'>)
- test_type_conversion_domain_array2 
-------------------------------------
-                                  4
-(1 row)
-
-select test_type_conversion_domain_array2(array[4,2]);  -- fail
-ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
-CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
-  RETURNS ordered_pair_domain AS $$
-plpy.info(x, type(x))
-return x[0]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
-INFO:  ([[2, 4]], <class 'list'>)
- test_type_conversion_array_domain_array 
------------------------------------------
- {2,4}
-(1 row)
-
----
---- Composite types
----
-CREATE TABLE employee (
-    name text,
-    basesalary integer,
-    bonus integer
-);
-INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
-CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
-return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpython3u;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
- name | test_composite_table_input 
-------+----------------------------
- John |                        110
- Mary |                        210
-(2 rows)
-
-ALTER TABLE employee DROP bonus;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
-ERROR:  KeyError: 'bonus'
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "test_composite_table_input", line 2, in <module>
-    return e['basesalary'] + e['bonus']
-PL/Python function "test_composite_table_input"
-ALTER TABLE employee ADD bonus integer;
-UPDATE employee SET bonus = 10;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
- name | test_composite_table_input 
-------+----------------------------
- John |                        110
- Mary |                        210
-(2 rows)
-
-CREATE TYPE named_pair AS (
-    i integer,
-    j integer
-);
-CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
-return sum(p.values())
-$$ LANGUAGE plpython3u;
-SELECT test_composite_type_input(row(1, 2));
- test_composite_type_input 
----------------------------
-                         3
-(1 row)
-
-ALTER TYPE named_pair RENAME TO named_pair_2;
-SELECT test_composite_type_input(row(1, 2));
- test_composite_type_input 
----------------------------
-                         3
-(1 row)
-
---
--- Domains within composite
---
-CREATE TYPE nnint_container AS (f1 int, f2 nnint);
-CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
-return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
-SELECT nnint_test(null, 3);
- nnint_test 
-------------
- (,3)
-(1 row)
-
-SELECT nnint_test(3, null);  -- fail
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-CONTEXT:  while creating return value
-PL/Python function "nnint_test"
---
--- Domains of composite
---
-CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
-CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
-return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
-SELECT read_ordered_named_pair(row(1, 2));
- read_ordered_named_pair 
--------------------------
-                       3
-(1 row)
-
-SELECT read_ordered_named_pair(row(2, 1));  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
-return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
-SELECT build_ordered_named_pair(1,2);
- build_ordered_named_pair 
---------------------------
- (1,2)
-(1 row)
-
-SELECT build_ordered_named_pair(2,1);  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CONTEXT:  while creating return value
-PL/Python function "build_ordered_named_pair"
-CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
-return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
-SELECT build_ordered_named_pairs(1,2);
- build_ordered_named_pairs 
----------------------------
- {"(1,2)","(1,3)"}
-(1 row)
-
-SELECT build_ordered_named_pairs(2,1);  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CONTEXT:  while creating return value
-PL/Python function "build_ordered_named_pairs"
---
--- Prepared statements
---
-CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
-rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
-return rv[0]['val']
-$$;
-SELECT test_prep_bool_input(); -- 1
- test_prep_bool_input 
-----------------------
-                    1
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
-rv = plpy.execute(plan, [0], 5)
-plpy.info(rv[0])
-return rv[0]['val']
-$$;
-SELECT test_prep_bool_output(); -- false
-INFO:  {'val': False}
- test_prep_bool_output 
------------------------
- f
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
-rv = plpy.execute(plan, [bb], 5)
-return rv[0]['val']
-$$;
-SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
- test_prep_bytea_input 
------------------------
-                     3
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
-rv = plpy.execute(plan, [], 5)
-plpy.info(rv[0])
-return rv[0]['val']
-$$;
-SELECT test_prep_bytea_output();
-INFO:  {'val': b'\xaa\x00\xbb'}
- test_prep_bytea_output 
-------------------------
- \xaa00bb
-(1 row)
-
diff --git a/src/pl/plpython/expected/plpython_unicode.out b/src/pl/plpython/expected/plpython_unicode.out
index c7546dd4587..fd54b0b88e8 100644
--- a/src/pl/plpython/expected/plpython_unicode.out
+++ b/src/pl/plpython/expected/plpython_unicode.out
@@ -11,24 +11,24 @@ CREATE TABLE unicode_test (
 	testvalue  text NOT NULL
 );
 CREATE FUNCTION unicode_return() RETURNS text AS E'
-return u"\\xA0"
-' LANGUAGE plpythonu;
+return "\\xA0"
+' LANGUAGE plpython3u;
 CREATE FUNCTION unicode_trigger() RETURNS trigger AS E'
-TD["new"]["testvalue"] = u"\\xA0"
+TD["new"]["testvalue"] = "\\xA0"
 return "MODIFY"
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test
   FOR EACH ROW EXECUTE PROCEDURE unicode_trigger();
 CREATE FUNCTION unicode_plan1() RETURNS text AS E'
 plan = plpy.prepare("SELECT $1 AS testvalue", ["text"])
-rv = plpy.execute(plan, [u"\\xA0"], 1)
+rv = plpy.execute(plan, ["\\xA0"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 CREATE FUNCTION unicode_plan2() RETURNS text AS E'
-plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"])
+plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"])
 rv = plpy.execute(plan, ["foo", "bar"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 SELECT unicode_return();
  unicode_return 
 ----------------
diff --git a/src/pl/plpython/expected/plpython_void.out b/src/pl/plpython/expected/plpython_void.out
index 1080d12d6b2..07d0760783e 100644
--- a/src/pl/plpython/expected/plpython_void.out
+++ b/src/pl/plpython/expected/plpython_void.out
@@ -3,14 +3,14 @@
 --
 CREATE FUNCTION test_void_func1() RETURNS void AS $$
 x = 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- illegal: can't return non-None value in void-returning func
 CREATE FUNCTION test_void_func2() RETURNS void AS $$
 return 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_return_none() RETURNS int AS $$
 None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- Tests for functions returning void
 SELECT test_void_func1(), test_void_func1() IS NULL AS "is null";
  test_void_func1 | is null 
diff --git a/src/pl/plpython/regress-python3-mangle.mk b/src/pl/plpython/regress-python3-mangle.mk
deleted file mode 100644
index a785818a172..00000000000
--- a/src/pl/plpython/regress-python3-mangle.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-ifeq ($(python_majorversion),3)
-# Adjust regression tests for Python 3 compatibility
-#
-# Mention those regression test files that need to be mangled in the
-# variable REGRESS_PLPYTHON3_MANGLE.  They will be copied to a
-# subdirectory python3/ and have their Python syntax and other bits
-# adjusted to work with Python 3.
-
-# Note that the order of the tests needs to be preserved in this
-# expression.
-REGRESS := $(foreach test,$(REGRESS),$(if $(filter $(test),$(REGRESS_PLPYTHON3_MANGLE)),python3/$(test),$(test)))
-
-.PHONY: pgregress-python3-mangle
-pgregress-python3-mangle:
-	$(MKDIR_P) sql/python3 expected/python3 results/python3
-	for file in $(patsubst %,$(srcdir)/sql/%.sql,$(REGRESS_PLPYTHON3_MANGLE)) $(patsubst %,$(srcdir)/expected/%*.out,$(REGRESS_PLPYTHON3_MANGLE)); do \
-	  sed \
-	      -e "s/<type 'exceptions\.\([[:alpha:]]*\)'>/<class '\1'>/g" \
-	      -e "s/<type 'long'>/<class 'int'>/g" \
-	      -e "s/\([0-9][0-9]*\)L/\1/g" \
-	      -e 's/\([ [{]\)u"/\1"/g' \
-	      -e "s/\([ [{]\)u'/\1'/g" \
-	      -e "s/def next/def __next__/g" \
-	      -e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
-	      -e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
-	      -e "s/EXTENSION plpythonu/EXTENSION plpython3u/g" \
-	      -e "s/EXTENSION plpython2u/EXTENSION plpython3u/g" \
-	      -e "s/EXTENSION \([^ ]*\)_plpythonu/EXTENSION \1_plpython3u/g" \
-	      -e "s/EXTENSION \([^ ]*\)_plpython2u/EXTENSION \1_plpython3u/g" \
-	      -e 's/installing required extension "plpython2u"/installing required extension "plpython3u"/g' \
-	    $$file >`echo $$file | sed 's,^.*/\([^/][^/]*/\)\([^/][^/]*\)$$,\1python3/\2,'` || exit; \
-	done
-
-check installcheck: pgregress-python3-mangle
-
-pg_regress_clean_files += sql/python3/ expected/python3/ results/python3/
-
-endif # Python 3
diff --git a/src/pl/plpython/sql/plpython_call.sql b/src/pl/plpython/sql/plpython_call.sql
index b0b3705ae3c..daa4bc377d7 100644
--- a/src/pl/plpython/sql/plpython_call.sql
+++ b/src/pl/plpython/sql/plpython_call.sql
@@ -3,7 +3,7 @@
 --
 
 CREATE PROCEDURE test_proc1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 pass
 $$;
@@ -13,7 +13,7 @@ CALL test_proc1();
 
 -- error: can't return non-None
 CREATE PROCEDURE test_proc2()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return 5
 $$;
@@ -24,7 +24,7 @@ CALL test_proc2();
 CREATE TABLE test1 (a int);
 
 CREATE PROCEDURE test_proc3(x int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("INSERT INTO test1 VALUES (%s)" % x)
 $$;
@@ -37,7 +37,7 @@ SELECT * FROM test1;
 -- output arguments
 
 CREATE PROCEDURE test_proc5(INOUT a text)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return [a + '+' + a]
 $$;
@@ -46,7 +46,7 @@ CALL test_proc5('abc');
 
 
 CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return (b * a, c * a)
 $$;
@@ -57,7 +57,7 @@ CALL test_proc6(2, 3, 4);
 -- OUT parameters
 
 CREATE PROCEDURE test_proc9(IN a int, OUT b int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.notice("a: %s" % (a))
 return (a * 2,)
diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql
index 0fd2f5d5e3b..21757701cc1 100644
--- a/src/pl/plpython/sql/plpython_composite.sql
+++ b/src/pl/plpython/sql/plpython_composite.sql
@@ -1,6 +1,6 @@
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT multiout_simple();
 SELECT * FROM multiout_simple();
@@ -9,7 +9,7 @@ SELECT (multiout_simple()).j + 3;
 
 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT multiout_simple_setof();
 SELECT * FROM multiout_simple_setof();
@@ -34,7 +34,7 @@ elif typ == 'obj':
     return type_record
 elif typ == 'str':
     return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
 SELECT multiout_record_as('dict', 'foo', 1, 'f');
@@ -77,7 +77,7 @@ for i in range(n):
     power = 2 ** i
     length = plpy.execute("select length('%d')" % power)[0]['length']
     yield power, length
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_setof(3);
 SELECT multiout_setof(5);
@@ -86,7 +86,7 @@ CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
 return [{'x': 4, 'y' :'four'},
         {'x': 7, 'y' :'seven'},
         {'x': 0, 'y' :'zero'}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_return_table();
 
@@ -94,18 +94,18 @@ CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS
 yield [[1], 'a']
 yield [[1,2], 'b']
 yield [[1,2,3], None]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_array();
 
 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
 return {'first': 1, 'second': 2}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
 return [{'first': 1, 'second': 2},
        {'first': 3, 'second': 4	}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM singleout_composite();
 SELECT * FROM multiout_composite();
@@ -113,7 +113,7 @@ SELECT * FROM multiout_composite();
 -- composite OUT parameters in functions returning RECORD not supported yet
 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
 return (n, (n * 2, n * 3))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
 if returnnull:
@@ -132,7 +132,7 @@ elif typ == 'str':
     d = "(%r,%r)" % (n * 2, n * 3)
 for i in range(n):
     yield (i, d)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_composite(2);
 SELECT * FROM multiout_table_type_setof('dict', 'f', 3);
@@ -157,7 +157,7 @@ CREATE TABLE changing (
 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
 return [(1, {'i': 1, 'j': 2}),
         (1, (3, 4))]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM changing_test();
 ALTER TABLE changing DROP COLUMN j;
@@ -178,14 +178,14 @@ yield {'tab': [('first', 1), ('second', 2)],
 yield {'tab': [('first', 1), ('second', 2)],
       'typ': [{'first': 'third', 'second': 3},
               {'first': 'fourth', 'second': 4}]}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM composite_types_table();
 
 -- check what happens if the output record descriptor changes
 CREATE FUNCTION return_record(t text) RETURNS record AS $$
 return {'t': t, 'val': 10}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM return_record('abc') AS r(t text, val integer);
 SELECT * FROM return_record('abc') AS r(t text, val bigint);
@@ -196,7 +196,7 @@ SELECT * FROM return_record('999') AS r(val text, t integer);
 
 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
 return {'v1':1,'v2':2,t:3}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
 SELECT * FROM return_record_2('v3') AS (v2 int, v3 int, v1 int);
@@ -211,7 +211,7 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
 -- multi-dimensional array of composite types.
 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list();
 
 -- Starting with PostgreSQL 10, a composite type in an array cannot be
@@ -220,5 +220,5 @@ SELECT * FROM composite_type_as_list();
 -- on the issue.
 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
   return [['first', 1]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list_broken();
diff --git a/src/pl/plpython/sql/plpython_do.sql b/src/pl/plpython/sql/plpython_do.sql
index 0e281a08ee8..d49413268e9 100644
--- a/src/pl/plpython/sql/plpython_do.sql
+++ b/src/pl/plpython/sql/plpython_do.sql
@@ -1,5 +1,3 @@
-DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
+DO $$ plpy.notice("This is plpython3u.") $$ LANGUAGE plpython3u;
 
-DO $$ plpy.notice("This is plpython2u.") $$ LANGUAGE plpython2u;
-
-DO $$ raise Exception("error test") $$ LANGUAGE plpythonu;
+DO $$ raise Exception("error test") $$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/sql/plpython_drop.sql b/src/pl/plpython/sql/plpython_drop.sql
index 72d5d657ec3..e4f373b2bc7 100644
--- a/src/pl/plpython/sql/plpython_drop.sql
+++ b/src/pl/plpython/sql/plpython_drop.sql
@@ -3,6 +3,4 @@
 --
 SET client_min_messages = WARNING;
 
-DROP EXTENSION plpythonu CASCADE;
-
-DROP EXTENSION IF EXISTS plpython2u CASCADE;
+DROP EXTENSION plpython3u CASCADE;
diff --git a/src/pl/plpython/sql/plpython_ereport.sql b/src/pl/plpython/sql/plpython_ereport.sql
index 58df2057ef5..d4f6223e59d 100644
--- a/src/pl/plpython/sql/plpython_ereport.sql
+++ b/src/pl/plpython/sql/plpython_ereport.sql
@@ -17,28 +17,28 @@ plpy.info('This is message text.',
 plpy.notice('notice', detail='some detail')
 plpy.warning('warning', detail='some detail')
 plpy.error('stop on error', detail='some detail', hint='some hint')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT elog_test();
 
-DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpythonu;
+DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u;
 
 DO $$
 import time;
 from datetime import date
 plpy.info('other types', detail=date(2016, 2, 26))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 plpy.info('other types', detail=basket)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- should fail
-DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u;
 
 -- raise exception in python, handle exception in plgsql
 CREATE OR REPLACE FUNCTION raise_exception(_message text, _detail text DEFAULT NULL, _hint text DEFAULT NULL,
@@ -57,7 +57,7 @@ kwargs = {
 }
 # ignore None values
 plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT raise_exception('hello', 'world');
 SELECT raise_exception('message text', 'detail text', _sqlstate => 'YY333');
@@ -118,17 +118,13 @@ BEGIN
 END;
 $$;
 
--- The displayed context is different between Python2 and Python3,
--- but that's not important for this test.
-\set SHOW_CONTEXT never
-
 DO $$
 try:
     plpy.execute("select raise_exception(_message => 'my message', _sqlstate => 'XX987', _hint => 'some hint', _table_name => 'users_tab', _datatype_name => 'user_type')")
 except Exception as e:
     plpy.info(e.spidata)
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 try:
@@ -136,4 +132,4 @@ try:
 except Exception as e:
     plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name))
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index 88d6936fd0d..11f14ec5a7c 100644
--- a/src/pl/plpython/sql/plpython_error.sql
+++ b/src/pl/plpython/sql/plpython_error.sql
@@ -7,7 +7,7 @@
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 /* With check_function_bodies = false the function should get defined
  * and the error reported when called
@@ -17,7 +17,7 @@ SET check_function_bodies = false;
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 SELECT python_syntax_error();
 /* Run the function twice to check if the hashtable entry gets cleaned up */
@@ -30,7 +30,7 @@ RESET check_function_bodies;
 CREATE FUNCTION sql_syntax_error() RETURNS text
         AS
 'plpy.execute("syntax error")'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 SELECT sql_syntax_error();
 
@@ -40,7 +40,7 @@ SELECT sql_syntax_error();
 CREATE FUNCTION exception_index_invalid(text) RETURNS text
 	AS
 'return args[1]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT exception_index_invalid('test');
 
@@ -51,7 +51,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text
 	AS
 'rv = plpy.execute("SELECT test5(''foo'')")
 return rv[0]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT exception_index_invalid_nested();
 
@@ -68,7 +68,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_uncaught('rick');
 
@@ -90,7 +90,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_caught('rick');
 
@@ -111,7 +111,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_reraised('rick');
 
@@ -127,7 +127,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT valid_type('rick');
 
@@ -147,7 +147,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_error();
 
@@ -167,7 +167,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_error_raise();
 
@@ -187,7 +187,7 @@ def fun3():
 fun3()
 return "you''ve been warned"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_warning();
 
@@ -196,7 +196,7 @@ SELECT nested_warning();
 CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
 $$
 plpy.nonexistent
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT toplevel_attribute_error();
 
@@ -213,7 +213,7 @@ def third():
   plpy.execute("select sql_error()")
 
 first()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
 begin
@@ -229,7 +229,7 @@ $$ LANGUAGE plpgsql;
 
 CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
 plpy.execute("select sql_error()")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT python_traceback();
 SELECT sql_error();
@@ -251,7 +251,7 @@ except spiexceptions.NotNullViolation as e:
     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
 except spiexceptions.UniqueViolation as e:
     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT specific_exception(2);
 SELECT specific_exception(NULL);
@@ -262,7 +262,7 @@ SELECT specific_exception(2);
 CREATE FUNCTION python_unique_violation() RETURNS void AS $$
 plpy.execute("insert into specific values (1)")
 plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
 begin
@@ -283,7 +283,7 @@ CREATE FUNCTION manual_subxact() RETURNS void AS $$
 plpy.execute("savepoint save")
 plpy.execute("create table foo(x integer)")
 plpy.execute("rollback to save")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT manual_subxact();
 
@@ -295,7 +295,7 @@ rollback = plpy.prepare("rollback to save")
 plpy.execute(save)
 plpy.execute("create table foo(x integer)")
 plpy.execute(rollback)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT manual_subxact_prepared();
 
@@ -303,7 +303,7 @@ SELECT manual_subxact_prepared();
  */
 CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
 raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 BEGIN
@@ -319,7 +319,7 @@ CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
 exc = plpy.spiexceptions.DivisionByZero()
 exc.sqlstate = 'SILLY'
 raise exc
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 BEGIN
@@ -332,14 +332,14 @@ $$ LANGUAGE plpgsql;
 /* test the context stack trace for nested execution levels
  */
 CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
 plpy.execute("SELECT notice_innerfunc()")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 \set SHOW_CONTEXT always
 
diff --git a/src/pl/plpython/sql/plpython_global.sql b/src/pl/plpython/sql/plpython_global.sql
index 32502b41eee..96d20492861 100644
--- a/src/pl/plpython/sql/plpython_global.sql
+++ b/src/pl/plpython/sql/plpython_global.sql
@@ -9,7 +9,7 @@ CREATE FUNCTION global_test_one() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_one"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 CREATE FUNCTION global_test_two() returns text
     AS
@@ -18,7 +18,7 @@ CREATE FUNCTION global_test_two() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_two"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 CREATE FUNCTION static_test() returns int4
@@ -29,7 +29,7 @@ else:
 	SD["call"] = 1
 return SD["call"]
 '
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 SELECT static_test();
diff --git a/src/pl/plpython/sql/plpython_import.sql b/src/pl/plpython/sql/plpython_import.sql
index ec887677e1e..3031eef2e69 100644
--- a/src/pl/plpython/sql/plpython_import.sql
+++ b/src/pl/plpython/sql/plpython_import.sql
@@ -7,7 +7,7 @@ CREATE FUNCTION import_fail() returns text
 except ImportError:
 	return "failed as expected"
 return "succeeded, that wasn''t supposed to happen"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 CREATE FUNCTION import_succeed() returns text
@@ -28,7 +28,7 @@ except Exception as ex:
 	plpy.notice("import failed -- %s" % str(ex))
 	return "failed, that wasn''t supposed to happen"
 return "succeeded, as expected"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 CREATE FUNCTION import_test_one(p text) RETURNS text
 	AS
@@ -39,7 +39,7 @@ except ImportError:
     import sha
     digest = sha.new(p)
 return digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION import_test_two(u users) RETURNS text
 	AS
@@ -51,7 +51,7 @@ except ImportError:
     import sha
     digest = sha.new(plain);
 return "sha hash of " + plain + " is " + digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 -- import python modules
diff --git a/src/pl/plpython/sql/plpython_newline.sql b/src/pl/plpython/sql/plpython_newline.sql
index f9cee9491bb..cb22ba923f9 100644
--- a/src/pl/plpython/sql/plpython_newline.sql
+++ b/src/pl/plpython/sql/plpython_newline.sql
@@ -4,15 +4,15 @@
 
 CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS
 E'x = 100\ny = 23\nreturn x + y\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS
 E'x = 100\ry = 23\rreturn x + y\r'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS
 E'x = 100\r\ny = 23\r\nreturn x + y\r\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 
 SELECT newline_lf();
diff --git a/src/pl/plpython/sql/plpython_params.sql b/src/pl/plpython/sql/plpython_params.sql
index ee75c4dc410..8bab4888592 100644
--- a/src/pl/plpython/sql/plpython_params.sql
+++ b/src/pl/plpython/sql/plpython_params.sql
@@ -4,13 +4,13 @@
 
 CREATE FUNCTION test_param_names0(integer, integer) RETURNS int AS $$
 return args[0] + args[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_param_names1(a0 integer, a1 text) RETURNS boolean AS $$
 assert a0 == args[0]
 assert a1 == args[1]
 return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_param_names2(u users) RETURNS text AS $$
 assert u == args[0]
@@ -22,7 +22,7 @@ if isinstance(u, dict):
 else:
     s = str(u)
 return s
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- use deliberately wrong parameter names
 CREATE FUNCTION test_param_names3(a0 integer) RETURNS boolean AS $$
@@ -32,7 +32,7 @@ try:
 except NameError as e:
 	assert e.args[0].find("a1") > -1
 	return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 SELECT test_param_names0(2,7);
diff --git a/src/pl/plpython/sql/plpython_quote.sql b/src/pl/plpython/sql/plpython_quote.sql
index 346b5485daf..a1133e7e266 100644
--- a/src/pl/plpython/sql/plpython_quote.sql
+++ b/src/pl/plpython/sql/plpython_quote.sql
@@ -9,7 +9,7 @@ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
         return plpy.quote_ident(t)
     else:
         raise plpy.Error("unrecognized quote type %s" % how)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT quote(t, 'literal') FROM (VALUES
        ('abc'),
diff --git a/src/pl/plpython/sql/plpython_record.sql b/src/pl/plpython/sql/plpython_record.sql
index 9bab4c9e82d..52bad8bccea 100644
--- a/src/pl/plpython/sql/plpython_record.sql
+++ b/src/pl/plpython/sql/plpython_record.sql
@@ -27,7 +27,7 @@ elif typ == 'obj':
 	type_record.first = first
 	type_record.second = second
 	return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
 if retnull:
@@ -45,20 +45,20 @@ elif typ == 'obj':
 	return type_record
 elif typ == 'str':
 	return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
 return first + '_in_to_out';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 -- Test tuple returning functions
@@ -136,14 +136,14 @@ SELECT * FROM test_type_record_as('obj', 'one', 1, false);
 
 CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
     return { 'first': 'first' }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error1();
 
 
 CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
     return [ 'first' ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error2();
 
@@ -152,12 +152,12 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
     class type_record: pass
     type_record.first = 'first'
     return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error3();
 
 CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
     return 'foo'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error4();
diff --git a/src/pl/plpython/sql/plpython_setof.sql b/src/pl/plpython/sql/plpython_setof.sql
index 16c2eef0ad6..4cfb10192c0 100644
--- a/src/pl/plpython/sql/plpython_setof.sql
+++ b/src/pl/plpython/sql/plpython_setof.sql
@@ -4,21 +4,21 @@
 
 CREATE FUNCTION test_setof_error() RETURNS SETOF text AS $$
 return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_setof_error();
 
 
 CREATE FUNCTION test_setof_as_list(count integer, content text) RETURNS SETOF text AS $$
 return [ content ]*count
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_as_tuple(count integer, content text) RETURNS SETOF text AS $$
 t = ()
 for i in range(count):
 	t += ( content, )
 return t
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_as_iterator(count integer, content text) RETURNS SETOF text AS $$
 class producer:
@@ -27,13 +27,13 @@ class producer:
 		self.icount = icount
 	def __iter__ (self):
 		return self
-	def next (self):
+	def __next__ (self):
 		if self.icount == 0:
 			raise StopIteration
 		self.icount -= 1
 		return self.icontent
 return producer(count, content)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_spi_in_iterator() RETURNS SETOF text AS
 $$
@@ -42,7 +42,7 @@ $$
         yield s
         plpy.execute('select 2')
 $$
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 
 -- Test set returning functions
@@ -69,7 +69,7 @@ global x
 while x <= lim:
     yield x
     x = x + 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT ugly(1, 5);
 
@@ -81,7 +81,7 @@ CREATE OR REPLACE FUNCTION get_user_records()
 RETURNS SETOF users
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT get_user_records();
 SELECT * FROM get_user_records();
@@ -91,7 +91,7 @@ CREATE OR REPLACE FUNCTION get_user_records2()
 RETURNS TABLE(fname text, lname text, username text, userid int)
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT get_user_records2();
 SELECT * FROM get_user_records2();
diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql
index dd77833ed56..fcd113acaa3 100644
--- a/src/pl/plpython/sql/plpython_spi.sql
+++ b/src/pl/plpython/sql/plpython_spi.sql
@@ -7,19 +7,19 @@ CREATE FUNCTION nested_call_one(a text) RETURNS text
 'q = "SELECT nested_call_two(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 CREATE FUNCTION nested_call_two(a text) RETURNS text
 	AS
 'q = "SELECT nested_call_three(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 CREATE FUNCTION nested_call_three(a text) RETURNS text
 	AS
 'return a'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 -- some spi stuff
 
@@ -35,7 +35,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
 	AS
@@ -49,7 +49,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
 	AS
@@ -64,7 +64,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION join_sequences(s sequences) RETURNS text
 	AS
@@ -77,7 +77,7 @@ for r in rv:
 	seq = seq + r["sequence"]
 return seq
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
 	AS
@@ -86,7 +86,7 @@ if a > 1:
     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
 return a + r
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 --
 -- spi and nested calls
@@ -120,7 +120,7 @@ if result.status() > 0:
    return result.nrows()
 else:
    return None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
 SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
@@ -129,7 +129,7 @@ CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return result.nrows()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_nrows_test($$SELECT 1$$);
 SELECT result_nrows_test($$CREATE TEMPORARY TABLE foo2 (a int, b text)$$);
@@ -140,7 +140,7 @@ CREATE FUNCTION result_len_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return len(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_len_test($$SELECT 1$$);
 SELECT result_len_test($$CREATE TEMPORARY TABLE foo3 (a int, b text)$$);
@@ -162,7 +162,7 @@ result[-1] = {'c': 1000}
 result[:2] = [{'c': 10}, {'c': 100}]
 plpy.info([item['c'] for item in result[:]])
 
-# raises TypeError, but the message differs on Python 2.6, so silence it
+# raises TypeError, catch so further tests could be added
 try:
     plpy.info(result['foo'])
 except TypeError:
@@ -170,7 +170,7 @@ except TypeError:
 else:
     assert False, "TypeError not raised"
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_subscript_test();
 
@@ -180,7 +180,7 @@ result = plpy.execute("select 1 where false")
 
 plpy.info(result[:])
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_empty_test();
 
@@ -189,7 +189,7 @@ AS $$
 plan = plpy.prepare(cmd)
 result = plpy.execute(plan)
 return str(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
 SELECT result_str_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
@@ -203,13 +203,13 @@ for row in res:
     if row['lname'] == 'doe':
         does += 1
 return does
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
 res.close()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -228,7 +228,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users order by fname")
@@ -242,7 +242,7 @@ except AttributeError:
 assert item['fname'] == 'rick'
 
 assert len(res.fetch(2)) == 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -253,7 +253,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION next_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -267,7 +267,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users where false")
@@ -281,7 +281,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
 plan = plpy.prepare(
@@ -291,13 +291,13 @@ for row in plpy.cursor(plan, ["w"]):
     yield row['fname']
 for row in plan.cursor(["j"]):
     yield row['fname']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
                     ["text"])
 c = plpy.cursor(plan, ["a", "b"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TYPE test_composite_type AS (
   a1 int,
@@ -308,7 +308,7 @@ CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS
 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
 return res[0]["c1"]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT simple_cursor_test();
 SELECT double_cursor_close();
diff --git a/src/pl/plpython/sql/plpython_subtransaction.sql b/src/pl/plpython/sql/plpython_subtransaction.sql
index cc4b1ae102b..c65c380f40c 100644
--- a/src/pl/plpython/sql/plpython_subtransaction.sql
+++ b/src/pl/plpython/sql/plpython_subtransaction.sql
@@ -17,7 +17,7 @@ with plpy.subtransaction():
         plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
     elif what_error == "Python":
         raise Exception("Python exception")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_ctx_test();
 SELECT * FROM subtransaction_tbl;
@@ -45,7 +45,7 @@ with plpy.subtransaction():
             raise
         plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_nested_test();
 SELECT * FROM subtransaction_tbl;
@@ -65,7 +65,7 @@ with plpy.subtransaction():
     plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
     plpy.execute("SELECT subtransaction_nested_test('t')")
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_deeply_nested_test();
 SELECT * FROM subtransaction_tbl;
@@ -76,25 +76,25 @@ TRUNCATE subtransaction_tbl;
 CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
 AS $$
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__exit__(None, None, None)
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
 AS $$
@@ -102,7 +102,7 @@ s = plpy.subtransaction()
 s.__enter__()
 s.__exit__(None, None, None)
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
 AS $$
@@ -110,14 +110,14 @@ s = plpy.subtransaction()
 s.__enter__()
 s.__enter__()
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- No warnings here, as the subtransaction gets indeed closed
 CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
 AS $$
 with plpy.subtransaction() as s:
     s.__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
 AS $$
@@ -126,7 +126,7 @@ try:
         s.__exit__(None, None, None)
 except ValueError as e:
     raise ValueError(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_exit_without_enter();
 SELECT subtransaction_enter_without_exit();
@@ -159,7 +159,7 @@ try:
     plpy.execute(p, ["wrong"])
 except plpy.SPIError:
     plpy.warning("Caught a SPI error")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_mix_explicit_and_implicit();
 SELECT * FROM subtransaction_tbl;
@@ -172,7 +172,7 @@ AS $$
 s = plpy.subtransaction()
 s.enter()
 s.exit(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_alternative_names();
 
@@ -186,7 +186,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT try_catch_inside_subtransaction();
 SELECT * FROM subtransaction_tbl;
@@ -202,7 +202,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT pk_violation_inside_subtransaction();
 SELECT * FROM subtransaction_tbl;
@@ -217,7 +217,7 @@ with plpy.subtransaction():
     cur.fetch(10)
 fetched = cur.fetch(10);
 return int(fetched[5]["i"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
 try:
@@ -229,7 +229,7 @@ except plpy.SPIError:
     fetched = cur.fetch(10)
     return int(fetched[5]["i"])
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
 try:
@@ -243,7 +243,7 @@ except plpy.SPIError:
     fetched = cur.fetch(5)
     return fetched[2]["i"]
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
 try:
@@ -254,7 +254,7 @@ except plpy.SPIError:
     cur.close()
     return True
 return False # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT cursor_in_subxact();
 SELECT cursor_aborted_subxact();
diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql
index 5f1be9c94a8..aa22a274155 100644
--- a/src/pl/plpython/sql/plpython_test.sql
+++ b/src/pl/plpython/sql/plpython_test.sql
@@ -1,13 +1,13 @@
 -- first some tests of basic functionality
-CREATE EXTENSION plpython2u;
+CREATE EXTENSION plpython3u;
 
 -- really stupid function just to get the module loaded
-CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
+CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 
 select stupid();
 
 -- check 2/3 versioning
-CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
+CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 
 select stupidn();
 
@@ -21,7 +21,7 @@ for key in keys:
     out.append("%s: %s" % (key, u[key]))
 words = a1 + " " + a2 + " => {" + ", ".join(out) + "}"
 return words'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 select "Argument test #1"(users, fname, lname) from users where lname = 'doe' order by 1;
 
@@ -32,7 +32,7 @@ $$
 contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
 contents.sort()
 return contents
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select module_contents();
 
@@ -47,6 +47,6 @@ plpy.info('info', 37, [1, 2, 3])
 plpy.notice('notice')
 plpy.warning('warning')
 plpy.error('error')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT elog_test_basic();
diff --git a/src/pl/plpython/sql/plpython_transaction.sql b/src/pl/plpython/sql/plpython_transaction.sql
index 68588d9fb02..c939ba76d48 100644
--- a/src/pl/plpython/sql/plpython_transaction.sql
+++ b/src/pl/plpython/sql/plpython_transaction.sql
@@ -2,7 +2,7 @@ CREATE TABLE test1 (a int, b text);
 
 
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -20,7 +20,7 @@ SELECT * FROM test1;
 TRUNCATE test1;
 
 DO
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -37,7 +37,7 @@ TRUNCATE test1;
 
 -- not allowed in a function
 CREATE FUNCTION transaction_test2() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -55,7 +55,7 @@ SELECT * FROM test1;
 
 -- also not allowed if procedure is called from a function
 CREATE FUNCTION transaction_test3() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("CALL transaction_test1()")
 return 1
@@ -68,9 +68,9 @@ SELECT * FROM test1;
 
 -- DO block inside function
 CREATE FUNCTION transaction_test4() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 return 1
 $$;
 
@@ -78,7 +78,7 @@ SELECT transaction_test4();
 
 
 -- commit inside subtransaction (prohibited)
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 s = plpy.subtransaction()
 s.enter()
 plpy.commit()
@@ -91,7 +91,7 @@ INSERT INTO test2 VALUES (0), (1), (2), (3), (4);
 
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.commit()
@@ -106,7 +106,7 @@ SELECT * FROM pg_cursors;
 -- error in cursor loop with commit
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x'])
     plpy.commit()
@@ -120,7 +120,7 @@ SELECT * FROM pg_cursors;
 -- rollback inside cursor loop
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.rollback()
@@ -134,7 +134,7 @@ SELECT * FROM pg_cursors;
 -- first commit then rollback inside cursor loop
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     if row['x'] % 2 == 0:
@@ -152,7 +152,7 @@ SELECT * FROM pg_cursors;
 CREATE TABLE testpk (id int PRIMARY KEY);
 CREATE TABLE testfk(f1 int REFERENCES testpk DEFERRABLE INITIALLY DEFERRED);
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 # this insert will fail during commit:
 plpy.execute("INSERT INTO testfk VALUES (0)")
 plpy.commit()
@@ -162,7 +162,7 @@ $$;
 SELECT * FROM testpk;
 SELECT * FROM testfk;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 # this insert will fail during commit:
 plpy.execute("INSERT INTO testfk VALUES (0)")
 try:
diff --git a/src/pl/plpython/sql/plpython_trigger.sql b/src/pl/plpython/sql/plpython_trigger.sql
index 19852dc5851..e5504b9ab1d 100644
--- a/src/pl/plpython/sql/plpython_trigger.sql
+++ b/src/pl/plpython/sql/plpython_trigger.sql
@@ -16,7 +16,7 @@ if TD["new"]["fname"] == "william":
 	TD["new"]["fname"] = TD["args"][0]
 	rv = "MODIFY"
 return rv'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE FUNCTION users_update() returns trigger
@@ -25,7 +25,7 @@ CREATE FUNCTION users_update() returns trigger
 	if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
 		return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE FUNCTION users_delete() RETURNS trigger
@@ -33,7 +33,7 @@ CREATE FUNCTION users_delete() RETURNS trigger
 'if TD["old"]["fname"] == TD["args"][0]:
 	return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
@@ -72,7 +72,7 @@ CREATE TABLE trigger_test_generated (
         j int GENERATED ALWAYS AS (i * 2) STORED
 );
 
-CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
 
 if 'relid' in TD:
 	TD['relid'] = "bogus:12345"
@@ -157,7 +157,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid1() RETURNS trigger
 AS $$
     return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger1
 BEFORE INSERT ON trigger_test
@@ -173,7 +173,7 @@ DROP TRIGGER stupid_trigger1 ON trigger_test;
 CREATE FUNCTION stupid2() RETURNS trigger
 AS $$
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger2
 BEFORE DELETE ON trigger_test
@@ -191,7 +191,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid3() RETURNS trigger
 AS $$
     return "foo"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
@@ -206,8 +206,8 @@ DROP TRIGGER stupid_trigger3 ON trigger_test;
 
 CREATE FUNCTION stupid3u() RETURNS trigger
 AS $$
-    return u"foo"
-$$ LANGUAGE plpythonu;
+    return "foo"
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
@@ -224,7 +224,7 @@ CREATE FUNCTION stupid4() RETURNS trigger
 AS $$
     del TD["new"]
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger4
 BEFORE UPDATE ON trigger_test
@@ -241,7 +241,7 @@ CREATE FUNCTION stupid5() RETURNS trigger
 AS $$
     TD["new"] = ['foo', 'bar']
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger5
 BEFORE UPDATE ON trigger_test
@@ -258,7 +258,7 @@ CREATE FUNCTION stupid6() RETURNS trigger
 AS $$
     TD["new"] = {1: 'foo', 2: 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger6
 BEFORE UPDATE ON trigger_test
@@ -275,7 +275,7 @@ CREATE FUNCTION stupid7() RETURNS trigger
 AS $$
     TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
@@ -290,9 +290,9 @@ DROP TRIGGER stupid_trigger7 ON trigger_test;
 
 CREATE FUNCTION stupid7u() RETURNS trigger
 AS $$
-    TD["new"] = {u'v': 'foo', u'a': 'bar'}
+    TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
@@ -318,7 +318,7 @@ CREATE FUNCTION test_null() RETURNS trigger
 AS $$
     TD["new"]['v'] = None
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER test_null_trigger
 BEFORE UPDATE ON trigger_test
@@ -341,7 +341,7 @@ SET DateStyle = 'ISO';
 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
 
@@ -365,7 +365,7 @@ CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
     TD['new']['f1'] = (3, False)
     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
@@ -380,7 +380,7 @@ CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
 
 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
@@ -399,7 +399,7 @@ CREATE TABLE composite_trigger_nested_test(c comp3);
 
 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
@@ -410,7 +410,7 @@ INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'
 SELECT * FROM composite_trigger_nested_test;
 
 -- check that using a function as a trigger over two tables works correctly
-CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
     TD["new"]["data"] = '1234'
     return 'MODIFY'
 $$;
@@ -432,7 +432,7 @@ SELECT * FROM b;
 CREATE TABLE transition_table_test (id int, name text);
 INSERT INTO transition_table_test VALUES (1, 'a');
 
-CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
 $$
     rv = plpy.execute("SELECT * FROM old_table")
     assert(rv.nrows() == 1)
@@ -455,7 +455,7 @@ DROP FUNCTION transition_table_test_f();
 -- dealing with generated columns
 
 CREATE FUNCTION generated_test_func1() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 TD['new']['j'] = 5  # not allowed
 return 'MODIFY'
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
index 0d207d9c015..40f4f79d99f 100644
--- a/src/pl/plpython/sql/plpython_types.sql
+++ b/src/pl/plpython/sql/plpython_types.sql
@@ -9,7 +9,7 @@
 CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bool(true);
 SELECT * FROM test_type_conversion_bool(false);
@@ -35,7 +35,7 @@ elif n == 5:
    ret = [0]
 plpy.info(ret, not not ret)
 return ret
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bool_other(0);
 SELECT * FROM test_type_conversion_bool_other(1);
@@ -48,7 +48,7 @@ SELECT * FROM test_type_conversion_bool_other(5);
 CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_char('a');
 SELECT * FROM test_type_conversion_char(null);
@@ -57,7 +57,7 @@ SELECT * FROM test_type_conversion_char(null);
 CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int2(100::int2);
 SELECT * FROM test_type_conversion_int2(-100::int2);
@@ -67,7 +67,7 @@ SELECT * FROM test_type_conversion_int2(null);
 CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int4(100);
 SELECT * FROM test_type_conversion_int4(-100);
@@ -77,7 +77,7 @@ SELECT * FROM test_type_conversion_int4(null);
 CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int8(100);
 SELECT * FROM test_type_conversion_int8(-100);
@@ -90,7 +90,7 @@ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
 # between decimal and cdecimal
 plpy.info(str(x), x.__class__.__name__)
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_numeric(100);
 SELECT * FROM test_type_conversion_numeric(-100);
@@ -105,7 +105,7 @@ SELECT * FROM test_type_conversion_numeric(null);
 CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_float4(100);
 SELECT * FROM test_type_conversion_float4(-100);
@@ -116,7 +116,7 @@ SELECT * FROM test_type_conversion_float4(null);
 CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_float8(100);
 SELECT * FROM test_type_conversion_float8(-100);
@@ -128,7 +128,7 @@ SELECT * FROM test_type_conversion_float8(100100100.654321);
 CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_oid(100);
 SELECT * FROM test_type_conversion_oid(2147483649);
@@ -138,7 +138,7 @@ SELECT * FROM test_type_conversion_oid(null);
 CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_text('hello world');
 SELECT * FROM test_type_conversion_text(null);
@@ -147,7 +147,7 @@ SELECT * FROM test_type_conversion_text(null);
 CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bytea('hello world');
 SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
@@ -157,7 +157,7 @@ SELECT * FROM test_type_conversion_bytea(null);
 CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
 import marshal
 return marshal.dumps('hello world')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
 import marshal
@@ -165,7 +165,7 @@ try:
     return marshal.loads(x)
 except ValueError as e:
     return 'FAILED: ' + str(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
 
@@ -178,7 +178,7 @@ CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
 
 CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_booltrue(true, true);
 SELECT * FROM test_type_conversion_booltrue(false, true);
@@ -190,7 +190,7 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
 CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
 SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
@@ -201,7 +201,7 @@ CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
 
 CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_nnint(10, 20);
 SELECT * FROM test_type_conversion_nnint(null, 20);
@@ -213,7 +213,7 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
 CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
 SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
@@ -229,7 +229,7 @@ SELECT * FROM test_type_conversion_bytea10('hello word', null);
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
@@ -243,14 +243,14 @@ SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
 CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
 
 CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
             [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
@@ -258,7 +258,7 @@ SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',
 CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
             [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
@@ -270,7 +270,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemint4(8,5,3,2);
 
@@ -278,7 +278,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemint8(5,5,3,2);
 
@@ -286,7 +286,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemfloat4(6,5,3,2);
 
@@ -294,14 +294,14 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemfloat8(7,5,3,2);
 
 CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
 SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
@@ -310,59 +310,59 @@ SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar
 CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
 
 
 CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_mixed1();
 
 
 CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_mixed2();
 
 CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
 return [[1,2,3],[4,5]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_mdarray_malformed();
 
 CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
 return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_mdarray_toodeep();
 
 
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_record();
 
 
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_string();
 
 CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
 return ('abc', 'def')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_tuple();
 
 CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
 return 5
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_error();
 
@@ -376,14 +376,14 @@ CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AN
 CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
 SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
 
 CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
 return [2,1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain_check_violation();
 
 
@@ -394,13 +394,13 @@ SELECT * FROM test_type_conversion_array_domain_check_violation();
 CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_read_uint2_array(array[1::uint2]);
 
 CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_build_uint2_array(1::int2);
 select test_build_uint2_array(-1::int2);  -- fail
@@ -413,7 +413,7 @@ select test_build_uint2_array(-1::int2);  -- fail
 CREATE FUNCTION test_type_conversion_domain_array(x integer[])
   RETURNS ordered_pair_domain[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_domain_array(array[2,4]);
 select test_type_conversion_domain_array(array[4,2]);  -- fail
@@ -422,7 +422,7 @@ CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
   RETURNS integer AS $$
 plpy.info(x, type(x))
 return x[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_domain_array2(array[2,4]);
 select test_type_conversion_domain_array2(array[4,2]);  -- fail
@@ -431,7 +431,7 @@ CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
   RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
 
@@ -450,7 +450,7 @@ INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
 
 CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
 return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT name, test_composite_table_input(employee.*) FROM employee;
 
@@ -470,7 +470,7 @@ CREATE TYPE named_pair AS (
 
 CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
 return sum(p.values())
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_composite_type_input(row(1, 2));
 
@@ -487,7 +487,7 @@ CREATE TYPE nnint_container AS (f1 int, f2 nnint);
 
 CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
 return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT nnint_test(null, 3);
 SELECT nnint_test(3, null);  -- fail
@@ -501,21 +501,21 @@ CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
 
 CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
 return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT read_ordered_named_pair(row(1, 2));
 SELECT read_ordered_named_pair(row(2, 1));  -- fail
 
 CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
 return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT build_ordered_named_pair(1,2);
 SELECT build_ordered_named_pair(2,1);  -- fail
 
 CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
 return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT build_ordered_named_pairs(1,2);
 SELECT build_ordered_named_pairs(2,1);  -- fail
@@ -526,7 +526,7 @@ SELECT build_ordered_named_pairs(2,1);  -- fail
 --
 
 CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
 rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
@@ -537,7 +537,7 @@ SELECT test_prep_bool_input(); -- 1
 
 
 CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
 rv = plpy.execute(plan, [0], 5)
@@ -549,7 +549,7 @@ SELECT test_prep_bool_output(); -- false
 
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
 rv = plpy.execute(plan, [bb], 5)
@@ -560,7 +560,7 @@ SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated
 
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
 rv = plpy.execute(plan, [], 5)
diff --git a/src/pl/plpython/sql/plpython_unicode.sql b/src/pl/plpython/sql/plpython_unicode.sql
index a11e5eeaa21..14f7b4e0053 100644
--- a/src/pl/plpython/sql/plpython_unicode.sql
+++ b/src/pl/plpython/sql/plpython_unicode.sql
@@ -14,28 +14,28 @@ CREATE TABLE unicode_test (
 );
 
 CREATE FUNCTION unicode_return() RETURNS text AS E'
-return u"\\xA0"
-' LANGUAGE plpythonu;
+return "\\xA0"
+' LANGUAGE plpython3u;
 
 CREATE FUNCTION unicode_trigger() RETURNS trigger AS E'
-TD["new"]["testvalue"] = u"\\xA0"
+TD["new"]["testvalue"] = "\\xA0"
 return "MODIFY"
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test
   FOR EACH ROW EXECUTE PROCEDURE unicode_trigger();
 
 CREATE FUNCTION unicode_plan1() RETURNS text AS E'
 plan = plpy.prepare("SELECT $1 AS testvalue", ["text"])
-rv = plpy.execute(plan, [u"\\xA0"], 1)
+rv = plpy.execute(plan, ["\\xA0"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 CREATE FUNCTION unicode_plan2() RETURNS text AS E'
-plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"])
+plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"])
 rv = plpy.execute(plan, ["foo", "bar"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 
 SELECT unicode_return();
diff --git a/src/pl/plpython/sql/plpython_void.sql b/src/pl/plpython/sql/plpython_void.sql
index 77d7f59e4c7..5a1a6711fb0 100644
--- a/src/pl/plpython/sql/plpython_void.sql
+++ b/src/pl/plpython/sql/plpython_void.sql
@@ -4,16 +4,16 @@
 
 CREATE FUNCTION test_void_func1() RETURNS void AS $$
 x = 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- illegal: can't return non-None value in void-returning func
 CREATE FUNCTION test_void_func2() RETURNS void AS $$
 return 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_return_none() RETURNS int AS $$
 None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 -- Tests for functions returning void
diff --git a/contrib/hstore_plpython/.gitignore b/contrib/hstore_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/hstore_plpython/.gitignore
+++ b/contrib/hstore_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile
index 19d99a8045e..9d88cda1d0f 100644
--- a/contrib/hstore_plpython/Makefile
+++ b/contrib/hstore_plpython/Makefile
@@ -10,7 +10,6 @@ EXTENSION = hstore_plpython3u
 DATA = hstore_plpython3u--1.0.sql
 
 REGRESS = hstore_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 PG_CPPFLAGS = $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
@@ -37,9 +36,4 @@ SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
 
 REGRESS_OPTS += --load-extension=hstore
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=hstore_plpythonu
-endif
 EXTRA_INSTALL += contrib/hstore
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out
index ecf1dd61bc1..bf238701fec 100644
--- a/contrib/hstore_plpython/expected/hstore_plpython.out
+++ b/contrib/hstore_plpython/expected/hstore_plpython.out
@@ -1,8 +1,8 @@
-CREATE EXTENSION hstore_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION hstore_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 -- test hstore -> python
 CREATE FUNCTION test1(val hstore) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -18,7 +18,7 @@ INFO:  [('aa', 'bb'), ('cc', None)]
 
 -- the same with the versioned language name
 CREATE FUNCTION test1n(val hstore) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -34,7 +34,7 @@ INFO:  [('aa', 'bb'), ('cc', None)]
 
 -- test hstore[] -> python
 CREATE FUNCTION test1arr(val hstore[]) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}])
@@ -48,7 +48,7 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']);
 
 -- test python -> hstore
 CREATE FUNCTION test2(a int, b text) RETURNS hstore
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = {'a': a, 'b': b, 'c': None}
@@ -65,14 +65,14 @@ SELECT test2(1, 'boo');
 CREATE OR REPLACE FUNCTION public.test2(a integer, b text)
  RETURNS hstore
  TRANSFORM FOR TYPE hstore
- LANGUAGE plpythonu
+ LANGUAGE plpython3u
 AS $function$
 val = {'a': a, 'b': b, 'c': None}
 return val
 $function$
 -- test python -> hstore[]
 CREATE FUNCTION test2arr() RETURNS hstore[]
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
@@ -87,7 +87,7 @@ SELECT test2arr();
 -- test python -> domain over hstore
 CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
 CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 return {'a': 1, fn: 'boo', 'c': None}
@@ -104,7 +104,7 @@ CONTEXT:  while creating return value
 PL/Python function "test2dom"
 -- test as part of prepare/execute
 CREATE FUNCTION test3() RETURNS void
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1")
@@ -131,7 +131,7 @@ SELECT * FROM test1;
 (1 row)
 
 CREATE FUNCTION test4() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}})
diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql
index b6d98b7dd53..a9cfbbe13e2 100644
--- a/contrib/hstore_plpython/sql/hstore_plpython.sql
+++ b/contrib/hstore_plpython/sql/hstore_plpython.sql
@@ -1,9 +1,9 @@
-CREATE EXTENSION hstore_plpython2u CASCADE;
+CREATE EXTENSION hstore_plpython3u CASCADE;
 
 
 -- test hstore -> python
 CREATE FUNCTION test1(val hstore) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -16,7 +16,7 @@ SELECT test1('aa=>bb, cc=>NULL'::hstore);
 
 -- the same with the versioned language name
 CREATE FUNCTION test1n(val hstore) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -29,7 +29,7 @@ SELECT test1n('aa=>bb, cc=>NULL'::hstore);
 
 -- test hstore[] -> python
 CREATE FUNCTION test1arr(val hstore[]) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}])
@@ -41,7 +41,7 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']);
 
 -- test python -> hstore
 CREATE FUNCTION test2(a int, b text) RETURNS hstore
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = {'a': a, 'b': b, 'c': None}
@@ -56,7 +56,7 @@ SELECT test2(1, 'boo');
 
 -- test python -> hstore[]
 CREATE FUNCTION test2arr() RETURNS hstore[]
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
@@ -70,7 +70,7 @@ SELECT test2arr();
 CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
 
 CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 return {'a': 1, fn: 'boo', 'c': None}
@@ -82,7 +82,7 @@ SELECT test2dom('bar');  -- fail
 
 -- test as part of prepare/execute
 CREATE FUNCTION test3() RETURNS void
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1")
@@ -103,7 +103,7 @@ INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL');
 SELECT * FROM test1;
 
 CREATE FUNCTION test4() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}})
diff --git a/contrib/jsonb_plpython/.gitignore b/contrib/jsonb_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/jsonb_plpython/.gitignore
+++ b/contrib/jsonb_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/jsonb_plpython/Makefile b/contrib/jsonb_plpython/Makefile
index 839b8f9a2fa..e3f160510da 100644
--- a/contrib/jsonb_plpython/Makefile
+++ b/contrib/jsonb_plpython/Makefile
@@ -12,7 +12,6 @@ EXTENSION = jsonb_plpython3u
 DATA = jsonb_plpython3u--1.0.sql
 
 REGRESS = jsonb_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -33,9 +32,3 @@ else
 rpathdir = $(python_libdir)
 SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
-
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=jsonb_plpythonu
-endif
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/jsonb_plpython/expected/jsonb_plpython.out b/contrib/jsonb_plpython/expected/jsonb_plpython.out
index b491fe9cc68..cac963de69c 100644
--- a/contrib/jsonb_plpython/expected/jsonb_plpython.out
+++ b/contrib/jsonb_plpython/expected/jsonb_plpython.out
@@ -1,8 +1,8 @@
-CREATE EXTENSION jsonb_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION jsonb_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 -- test jsonb -> python dict
 CREATE FUNCTION test1(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -18,7 +18,7 @@ SELECT test1('{"a": 1, "c": "NULL"}'::jsonb);
 -- test jsonb -> python dict
 -- complex dict with dicts as value
 CREATE FUNCTION test1complex(val jsonb) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -34,7 +34,7 @@ SELECT test1complex('{"d": {"d": 1}}'::jsonb);
 -- test jsonb[] -> python dict
 -- dict with array as value
 CREATE FUNCTION test1arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -50,7 +50,7 @@ SELECT test1arr('{"d":[12, 1]}'::jsonb);
 -- test jsonb[] -> python list
 -- simple list
 CREATE FUNCTION test2arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -66,7 +66,7 @@ SELECT test2arr('[12, 1]'::jsonb);
 -- test jsonb[] -> python list
 -- array of dicts
 CREATE FUNCTION test3arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -81,7 +81,7 @@ SELECT test3arr('[{"a": 1, "b": 2}, {"c": 3,"d": 4}]'::jsonb);
 
 -- test jsonb int -> python int
 CREATE FUNCTION test1int(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == 1)
@@ -95,7 +95,7 @@ SELECT test1int('1'::jsonb);
 
 -- test jsonb string -> python string
 CREATE FUNCTION test1string(val jsonb) RETURNS text
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == "a")
@@ -109,7 +109,7 @@ SELECT test1string('"a"'::jsonb);
 
 -- test jsonb null -> python None
 CREATE FUNCTION test1null(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == None)
@@ -123,7 +123,7 @@ SELECT test1null('null'::jsonb);
 
 -- test python -> jsonb
 CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 as $$
 return val
@@ -238,7 +238,7 @@ SELECT roundtrip('["string", "string2"]'::jsonb);
 
 -- complex numbers -> jsonb
 CREATE FUNCTION testComplexNumbers() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 1 + 2j
@@ -250,7 +250,7 @@ CONTEXT:  while creating return value
 PL/Python function "testcomplexnumbers"
 -- range -> jsonb
 CREATE FUNCTION testRange() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = range(3)
@@ -264,7 +264,7 @@ SELECT testRange();
 
 -- 0xff -> jsonb
 CREATE FUNCTION testDecimal() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 0xff
@@ -278,7 +278,7 @@ SELECT testDecimal();
 
 -- tuple -> jsonb
 CREATE FUNCTION testTuple() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = (1, 'String', None)
@@ -292,7 +292,7 @@ SELECT testTuple();
 
 -- interesting dict -> jsonb
 CREATE FUNCTION test_dict1() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = {"a": 1, None: 2, 33: 3}
diff --git a/contrib/jsonb_plpython/sql/jsonb_plpython.sql b/contrib/jsonb_plpython/sql/jsonb_plpython.sql
index 2ee1bca0a98..29dc33279a0 100644
--- a/contrib/jsonb_plpython/sql/jsonb_plpython.sql
+++ b/contrib/jsonb_plpython/sql/jsonb_plpython.sql
@@ -1,8 +1,8 @@
-CREATE EXTENSION jsonb_plpython2u CASCADE;
+CREATE EXTENSION jsonb_plpython3u CASCADE;
 
 -- test jsonb -> python dict
 CREATE FUNCTION test1(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -15,7 +15,7 @@ SELECT test1('{"a": 1, "c": "NULL"}'::jsonb);
 -- test jsonb -> python dict
 -- complex dict with dicts as value
 CREATE FUNCTION test1complex(val jsonb) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -29,7 +29,7 @@ SELECT test1complex('{"d": {"d": 1}}'::jsonb);
 -- test jsonb[] -> python dict
 -- dict with array as value
 CREATE FUNCTION test1arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -42,7 +42,7 @@ SELECT test1arr('{"d":[12, 1]}'::jsonb);
 -- test jsonb[] -> python list
 -- simple list
 CREATE FUNCTION test2arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -55,7 +55,7 @@ SELECT test2arr('[12, 1]'::jsonb);
 -- test jsonb[] -> python list
 -- array of dicts
 CREATE FUNCTION test3arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -67,7 +67,7 @@ SELECT test3arr('[{"a": 1, "b": 2}, {"c": 3,"d": 4}]'::jsonb);
 
 -- test jsonb int -> python int
 CREATE FUNCTION test1int(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == 1)
@@ -78,7 +78,7 @@ SELECT test1int('1'::jsonb);
 
 -- test jsonb string -> python string
 CREATE FUNCTION test1string(val jsonb) RETURNS text
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == "a")
@@ -89,7 +89,7 @@ SELECT test1string('"a"'::jsonb);
 
 -- test jsonb null -> python None
 CREATE FUNCTION test1null(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == None)
@@ -100,7 +100,7 @@ SELECT test1null('null'::jsonb);
 
 -- test python -> jsonb
 CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 as $$
 return val
@@ -129,7 +129,7 @@ SELECT roundtrip('["string", "string2"]'::jsonb);
 
 -- complex numbers -> jsonb
 CREATE FUNCTION testComplexNumbers() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 1 + 2j
@@ -140,7 +140,7 @@ SELECT testComplexNumbers();
 
 -- range -> jsonb
 CREATE FUNCTION testRange() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = range(3)
@@ -151,7 +151,7 @@ SELECT testRange();
 
 -- 0xff -> jsonb
 CREATE FUNCTION testDecimal() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 0xff
@@ -162,7 +162,7 @@ SELECT testDecimal();
 
 -- tuple -> jsonb
 CREATE FUNCTION testTuple() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = (1, 'String', None)
@@ -173,7 +173,7 @@ SELECT testTuple();
 
 -- interesting dict -> jsonb
 CREATE FUNCTION test_dict1() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = {"a": 1, None: 2, 33: 3}
diff --git a/contrib/ltree_plpython/.gitignore b/contrib/ltree_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/ltree_plpython/.gitignore
+++ b/contrib/ltree_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile
index 0bccb111e6b..406d2789c9c 100644
--- a/contrib/ltree_plpython/Makefile
+++ b/contrib/ltree_plpython/Makefile
@@ -10,7 +10,6 @@ EXTENSION = ltree_plpython3u
 DATA = ltree_plpython3u--1.0.sql
 
 REGRESS = ltree_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 PG_CPPFLAGS = $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
@@ -37,9 +36,4 @@ SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
 
 REGRESS_OPTS += --load-extension=ltree
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=ltree_plpythonu
-endif
 EXTRA_INSTALL += contrib/ltree
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/ltree_plpython/expected/ltree_plpython.out b/contrib/ltree_plpython/expected/ltree_plpython.out
index f28897fee48..bd32541fdb3 100644
--- a/contrib/ltree_plpython/expected/ltree_plpython.out
+++ b/contrib/ltree_plpython/expected/ltree_plpython.out
@@ -1,7 +1,7 @@
-CREATE EXTENSION ltree_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION ltree_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 CREATE FUNCTION test1(val ltree) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -15,7 +15,7 @@ INFO:  ['aa', 'bb', 'cc']
 (1 row)
 
 CREATE FUNCTION test1n(val ltree) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -29,7 +29,7 @@ INFO:  ['aa', 'bb', 'cc']
 (1 row)
 
 CREATE FUNCTION test2() RETURNS ltree
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 return ['foo', 'bar', 'baz']
diff --git a/contrib/ltree_plpython/sql/ltree_plpython.sql b/contrib/ltree_plpython/sql/ltree_plpython.sql
index 210f5428a5a..0b8d28399a6 100644
--- a/contrib/ltree_plpython/sql/ltree_plpython.sql
+++ b/contrib/ltree_plpython/sql/ltree_plpython.sql
@@ -1,8 +1,8 @@
-CREATE EXTENSION ltree_plpython2u CASCADE;
+CREATE EXTENSION ltree_plpython3u CASCADE;
 
 
 CREATE FUNCTION test1(val ltree) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -13,7 +13,7 @@ SELECT test1('aa.bb.cc'::ltree);
 
 
 CREATE FUNCTION test1n(val ltree) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -24,7 +24,7 @@ SELECT test1n('aa.bb.cc'::ltree);
 
 
 CREATE FUNCTION test2() RETURNS ltree
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 return ['foo', 'bar', 'baz']
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index e2b0db0879b..625f6fb88ae 100644
--- a/src/tools/msvc/vcregress.pl
+++ b/src/tools/msvc/vcregress.pl
@@ -314,55 +314,6 @@ sub taptest
 	return;
 }
 
-sub mangle_plpython3
-{
-	my $tests = shift;
-	mkdir "results" unless -d "results";
-	mkdir "sql/python3";
-	mkdir "results/python3";
-	mkdir "expected/python3";
-
-	foreach my $test (@$tests)
-	{
-		local $/ = undef;
-		foreach my $dir ('sql', 'expected')
-		{
-			my $extension = ($dir eq 'sql' ? 'sql' : 'out');
-
-			my @files =
-			  glob("$dir/$test.$extension $dir/${test}_[0-9].$extension");
-			foreach my $file (@files)
-			{
-				open(my $handle, '<', $file)
-				  || die "test file $file not found";
-				my $contents = <$handle>;
-				close($handle);
-				do
-				{
-					s/<type 'exceptions\.([[:alpha:]]*)'>/<class '$1'>/g;
-					s/<type 'long'>/<class 'int'>/g;
-					s/([0-9][0-9]*)L/$1/g;
-					s/([ [{])u"/$1"/g;
-					s/([ [{])u'/$1'/g;
-					s/def next/def __next__/g;
-					s/LANGUAGE plpython2?u/LANGUAGE plpython3u/g;
-					s/EXTENSION (\S*?)plpython2?u/EXTENSION $1plpython3u/g;
-					s/installing required extension "plpython2u"/installing required extension "plpython3u"/g;
-				  }
-				  for ($contents);
-				my $base = basename $file;
-				open($handle, '>', "$dir/python3/$base")
-				  || die "opening python 3 file for $file";
-				print $handle $contents;
-				close($handle);
-			}
-		}
-	}
-	do { s!^!python3/!; }
-	  foreach (@$tests);
-	return @$tests;
-}
-
 sub plcheck
 {
 	chdir "$topdir/src/pl";
@@ -386,8 +337,7 @@ sub plcheck
 		if ($lang eq 'plpython')
 		{
 			next
-			  unless -d "$topdir/$Config/plpython2"
-			  || -d "$topdir/$Config/plpython3";
+			  unless -d "$topdir/$Config/plpython3";
 			$lang = 'plpythonu';
 		}
 		else
@@ -397,8 +347,6 @@ sub plcheck
 		my @lang_args = ("--load-extension=$lang");
 		chdir $dir;
 		my @tests = fetchTests();
-		@tests = mangle_plpython3(\@tests)
-		  if $lang eq 'plpythonu' && -d "$topdir/$Config/plpython3";
 		if ($lang eq 'plperl')
 		{
 
@@ -462,28 +410,6 @@ sub subdircheck
 
 	my @opts = fetchRegressOpts();
 
-	# Special processing for python transform modules, see their respective
-	# Makefiles for more details regarding Python-version specific
-	# dependencies.
-	if ($module =~ /_plpython$/)
-	{
-		die "Python not enabled in configuration"
-		  if !defined($config->{python});
-
-		@opts = grep { $_ !~ /plpythonu/ } @opts;
-
-		if (-d "$topdir/$Config/plpython2")
-		{
-			push @opts, "--load-extension=plpythonu";
-			push @opts, '--load-extension=' . $module . 'u';
-		}
-		else
-		{
-			# must be python 3
-			@tests = mangle_plpython3(\@tests);
-		}
-	}
-
 	print "============================================================\n";
 	print "Checking $module\n";
 	my @args = (
-- 
2.35.1.354.g715d08a9e5

v6-0003-plpython-Code-cleanup-related-to-removal-of-Pytho.patchtext/x-diff; charset=us-asciiDownload
From 3beecdcb42a08f8ed6178cf09aab7bf227829df2 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 6 Mar 2022 16:57:28 -0800
Subject: [PATCH v6 3/4] plpython: Code cleanup related to removal of Python 2
 support.

TODO:
- figure out what to do with conflict handling in plpy_main.c

Reviewed-By: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 src/pl/plpython/plpy_cursorobject.c       |  8 ++--
 src/pl/plpython/plpy_elog.c               | 26 +++++------
 src/pl/plpython/plpy_exec.c               | 42 ++++++++---------
 src/pl/plpython/plpy_main.c               | 55 +++--------------------
 src/pl/plpython/plpy_planobject.c         |  2 +-
 src/pl/plpython/plpy_plpymodule.c         | 32 ++++---------
 src/pl/plpython/plpy_plpymodule.h         |  2 -
 src/pl/plpython/plpy_resultobject.c       | 16 ++-----
 src/pl/plpython/plpy_spi.c                | 10 ++---
 src/pl/plpython/plpy_typeio.c             | 28 +++++-------
 src/pl/plpython/plpy_util.c               |  9 ----
 src/pl/plpython/plpy_util.h               |  2 -
 src/pl/plpython/plpython.h                | 34 +-------------
 contrib/hstore_plpython/hstore_plpython.c | 12 ++---
 contrib/jsonb_plpython/jsonb_plpython.c   | 11 ++---
 contrib/ltree_plpython/ltree_plpython.c   |  6 +--
 16 files changed, 76 insertions(+), 219 deletions(-)

diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index 08d8b607e38..6b6e7433453 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -40,7 +40,7 @@ static PyTypeObject PLy_CursorType = {
 	.tp_name = "PLyCursor",
 	.tp_basicsize = sizeof(PLyCursorObject),
 	.tp_dealloc = PLy_cursor_dealloc,
-	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER,
+	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
 	.tp_doc = PLy_cursor_doc,
 	.tp_iter = PyObject_SelfIter,
 	.tp_iternext = PLy_cursor_iternext,
@@ -150,7 +150,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
 
 	if (args)
 	{
-		if (!PySequence_Check(args) || PyString_Check(args) || PyUnicode_Check(args))
+		if (!PySequence_Check(args) || PyUnicode_Check(args))
 		{
 			PLy_exception_set(PyExc_TypeError, "plpy.cursor takes a sequence as its second argument");
 			return NULL;
@@ -169,7 +169,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
 
 		if (!so)
 			PLy_elog(ERROR, "could not execute plan");
-		sv = PyString_AsString(so);
+		sv = PLyUnicode_AsString(so);
 		PLy_exception_set_plural(PyExc_TypeError,
 								 "Expected sequence of %d argument, got %d: %s",
 								 "Expected sequence of %d arguments, got %d: %s",
@@ -410,7 +410,7 @@ PLy_cursor_fetch(PyObject *self, PyObject *args)
 		SPI_cursor_fetch(portal, true, count);
 
 		Py_DECREF(ret->status);
-		ret->status = PyInt_FromLong(SPI_OK_FETCH);
+		ret->status = PyLong_FromLong(SPI_OK_FETCH);
 
 		Py_DECREF(ret->nrows);
 		ret->nrows = PyLong_FromUnsignedLongLong(SPI_processed);
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index 224b8836fba..e072643ca25 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -193,24 +193,20 @@ PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
 	e_type_o = PyObject_GetAttrString(e, "__name__");
 	e_module_o = PyObject_GetAttrString(e, "__module__");
 	if (e_type_o)
-		e_type_s = PyString_AsString(e_type_o);
+		e_type_s = PLyUnicode_AsString(e_type_o);
 	if (e_type_s)
-		e_module_s = PyString_AsString(e_module_o);
+		e_module_s = PLyUnicode_AsString(e_module_o);
 
 	if (v && ((vob = PyObject_Str(v)) != NULL))
-		vstr = PyString_AsString(vob);
+		vstr = PLyUnicode_AsString(vob);
 	else
 		vstr = "unknown";
 
 	initStringInfo(&xstr);
 	if (!e_type_s || !e_module_s)
 	{
-		if (PyString_Check(e))
-			/* deprecated string exceptions */
-			appendStringInfoString(&xstr, PyString_AsString(e));
-		else
-			/* shouldn't happen */
-			appendStringInfoString(&xstr, "unrecognized exception");
+		/* shouldn't happen */
+		appendStringInfoString(&xstr, "unrecognized exception");
 	}
 	/* mimics behavior of traceback.format_exception_only */
 	else if (strcmp(e_module_s, "builtins") == 0
@@ -290,11 +286,11 @@ PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
 			if (*tb_depth == 1)
 				fname = "<module>";
 			else
-				fname = PyString_AsString(name);
+				fname = PLyUnicode_AsString(name);
 
 			proname = PLy_procedure_name(exec_ctx->curr_proc);
-			plain_filename = PyString_AsString(filename);
-			plain_lineno = PyInt_AsLong(lineno);
+			plain_filename = PLyUnicode_AsString(filename);
+			plain_lineno = PyLong_AsLong(lineno);
 
 			if (proname == NULL)
 				appendStringInfo(&tbstr, "\n  PL/Python anonymous code block, line %ld, in %s",
@@ -365,7 +361,7 @@ PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
 	if (sqlstate == NULL)
 		return;
 
-	buffer = PyString_AsString(sqlstate);
+	buffer = PLyUnicode_AsString(sqlstate);
 	if (strlen(buffer) == 5 &&
 		strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
 	{
@@ -573,7 +569,7 @@ get_string_attr(PyObject *obj, char *attrname, char **str)
 	val = PyObject_GetAttrString(obj, attrname);
 	if (val != NULL && val != Py_None)
 	{
-		*str = pstrdup(PyString_AsString(val));
+		*str = pstrdup(PLyUnicode_AsString(val));
 	}
 	Py_XDECREF(val);
 }
@@ -589,7 +585,7 @@ set_string_attr(PyObject *obj, char *attrname, char *str)
 
 	if (str != NULL)
 	{
-		val = PyString_FromString(str);
+		val = PyUnicode_FromString(str);
 		if (!val)
 			return false;
 	}
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index c6f6a6fbcca..2cecfa28b12 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -360,9 +360,7 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
 		{
 			char	   *srv;
 
-			if (PyString_Check(plrv))
-				srv = PyString_AsString(plrv);
-			else if (PyUnicode_Check(plrv))
+			if (PyUnicode_Check(plrv))
 				srv = PLyUnicode_AsString(plrv);
 			else
 			{
@@ -700,35 +698,35 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 		if (!pltdata)
 			return NULL;
 
-		pltname = PyString_FromString(tdata->tg_trigger->tgname);
+		pltname = PyUnicode_FromString(tdata->tg_trigger->tgname);
 		PyDict_SetItemString(pltdata, "name", pltname);
 		Py_DECREF(pltname);
 
 		stroid = DatumGetCString(DirectFunctionCall1(oidout,
 													 ObjectIdGetDatum(tdata->tg_relation->rd_id)));
-		pltrelid = PyString_FromString(stroid);
+		pltrelid = PyUnicode_FromString(stroid);
 		PyDict_SetItemString(pltdata, "relid", pltrelid);
 		Py_DECREF(pltrelid);
 		pfree(stroid);
 
 		stroid = SPI_getrelname(tdata->tg_relation);
-		plttablename = PyString_FromString(stroid);
+		plttablename = PyUnicode_FromString(stroid);
 		PyDict_SetItemString(pltdata, "table_name", plttablename);
 		Py_DECREF(plttablename);
 		pfree(stroid);
 
 		stroid = SPI_getnspname(tdata->tg_relation);
-		plttableschema = PyString_FromString(stroid);
+		plttableschema = PyUnicode_FromString(stroid);
 		PyDict_SetItemString(pltdata, "table_schema", plttableschema);
 		Py_DECREF(plttableschema);
 		pfree(stroid);
 
 		if (TRIGGER_FIRED_BEFORE(tdata->tg_event))
-			pltwhen = PyString_FromString("BEFORE");
+			pltwhen = PyUnicode_FromString("BEFORE");
 		else if (TRIGGER_FIRED_AFTER(tdata->tg_event))
-			pltwhen = PyString_FromString("AFTER");
+			pltwhen = PyUnicode_FromString("AFTER");
 		else if (TRIGGER_FIRED_INSTEAD(tdata->tg_event))
-			pltwhen = PyString_FromString("INSTEAD OF");
+			pltwhen = PyUnicode_FromString("INSTEAD OF");
 		else
 		{
 			elog(ERROR, "unrecognized WHEN tg_event: %u", tdata->tg_event);
@@ -739,7 +737,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 
 		if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
 		{
-			pltlevel = PyString_FromString("ROW");
+			pltlevel = PyUnicode_FromString("ROW");
 			PyDict_SetItemString(pltdata, "level", pltlevel);
 			Py_DECREF(pltlevel);
 
@@ -750,7 +748,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 
 			if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
 			{
-				pltevent = PyString_FromString("INSERT");
+				pltevent = PyUnicode_FromString("INSERT");
 
 				PyDict_SetItemString(pltdata, "old", Py_None);
 				pytnew = PLy_input_from_tuple(&proc->result_in,
@@ -763,7 +761,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 			}
 			else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
 			{
-				pltevent = PyString_FromString("DELETE");
+				pltevent = PyUnicode_FromString("DELETE");
 
 				PyDict_SetItemString(pltdata, "new", Py_None);
 				pytold = PLy_input_from_tuple(&proc->result_in,
@@ -776,7 +774,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 			}
 			else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
 			{
-				pltevent = PyString_FromString("UPDATE");
+				pltevent = PyUnicode_FromString("UPDATE");
 
 				pytnew = PLy_input_from_tuple(&proc->result_in,
 											  tdata->tg_newtuple,
@@ -803,7 +801,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 		}
 		else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event))
 		{
-			pltlevel = PyString_FromString("STATEMENT");
+			pltlevel = PyUnicode_FromString("STATEMENT");
 			PyDict_SetItemString(pltdata, "level", pltlevel);
 			Py_DECREF(pltlevel);
 
@@ -812,13 +810,13 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 			*rv = NULL;
 
 			if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
-				pltevent = PyString_FromString("INSERT");
+				pltevent = PyUnicode_FromString("INSERT");
 			else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
-				pltevent = PyString_FromString("DELETE");
+				pltevent = PyUnicode_FromString("DELETE");
 			else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
-				pltevent = PyString_FromString("UPDATE");
+				pltevent = PyUnicode_FromString("UPDATE");
 			else if (TRIGGER_FIRED_BY_TRUNCATE(tdata->tg_event))
-				pltevent = PyString_FromString("TRUNCATE");
+				pltevent = PyUnicode_FromString("TRUNCATE");
 			else
 			{
 				elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
@@ -847,7 +845,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 			}
 			for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
 			{
-				pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]);
+				pltarg = PyUnicode_FromString(tdata->tg_trigger->tgargs[i]);
 
 				/*
 				 * stolen, don't Py_DECREF
@@ -931,9 +929,7 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 			PLyObToDatum *att;
 
 			platt = PyList_GetItem(plkeys, i);
-			if (PyString_Check(platt))
-				plattstr = PyString_AsString(platt);
-			else if (PyUnicode_Check(platt))
+			if (PyUnicode_Check(platt))
 				plattstr = PLyUnicode_AsString(platt);
 			else
 			{
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index 3eedaa80da7..21303c1586b 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -28,27 +28,13 @@
  * exported functions
  */
 
-#if PY_MAJOR_VERSION >= 3
-/* Use separate names to reduce confusion */
-#define plpython_validator plpython3_validator
-#define plpython_call_handler plpython3_call_handler
-#define plpython_inline_handler plpython3_inline_handler
-#endif
-
 extern void _PG_init(void);
 
 PG_MODULE_MAGIC;
 
-PG_FUNCTION_INFO_V1(plpython_validator);
-PG_FUNCTION_INFO_V1(plpython_call_handler);
-PG_FUNCTION_INFO_V1(plpython_inline_handler);
-
-#if PY_MAJOR_VERSION < 3
-/* Define aliases plpython2_call_handler etc */
-PG_FUNCTION_INFO_V1(plpython2_validator);
-PG_FUNCTION_INFO_V1(plpython2_call_handler);
-PG_FUNCTION_INFO_V1(plpython2_inline_handler);
-#endif
+PG_FUNCTION_INFO_V1(plpython3_validator);
+PG_FUNCTION_INFO_V1(plpython3_call_handler);
+PG_FUNCTION_INFO_V1(plpython3_inline_handler);
 
 
 static bool PLy_procedure_is_trigger(Form_pg_proc procStruct);
@@ -125,13 +111,9 @@ PLy_initialize(void)
 	if (inited)
 		return;
 
-#if PY_MAJOR_VERSION >= 3
 	PyImport_AppendInittab("plpy", PyInit_plpy);
-#endif
 	Py_Initialize();
-#if PY_MAJOR_VERSION >= 3
 	PyImport_ImportModule("plpy");
-#endif
 	PLy_init_interp();
 	PLy_init_plpy();
 	if (PyErr_Occurred())
@@ -171,7 +153,7 @@ PLy_init_interp(void)
 }
 
 Datum
-plpython_validator(PG_FUNCTION_ARGS)
+plpython3_validator(PG_FUNCTION_ARGS)
 {
 	Oid			funcoid = PG_GETARG_OID(0);
 	HeapTuple	tuple;
@@ -203,17 +185,8 @@ plpython_validator(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-#if PY_MAJOR_VERSION < 3
 Datum
-plpython2_validator(PG_FUNCTION_ARGS)
-{
-	/* call plpython validator with our fcinfo so it gets our oid */
-	return plpython_validator(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
-Datum
-plpython_call_handler(PG_FUNCTION_ARGS)
+plpython3_call_handler(PG_FUNCTION_ARGS)
 {
 	bool		nonatomic;
 	Datum		retval;
@@ -284,16 +257,8 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 	return retval;
 }
 
-#if PY_MAJOR_VERSION < 3
 Datum
-plpython2_call_handler(PG_FUNCTION_ARGS)
-{
-	return plpython_call_handler(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
-Datum
-plpython_inline_handler(PG_FUNCTION_ARGS)
+plpython3_inline_handler(PG_FUNCTION_ARGS)
 {
 	LOCAL_FCINFO(fake_fcinfo, 0);
 	InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
@@ -368,14 +333,6 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-#if PY_MAJOR_VERSION < 3
-Datum
-plpython2_inline_handler(PG_FUNCTION_ARGS)
-{
-	return plpython_inline_handler(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
 static bool
 PLy_procedure_is_trigger(Form_pg_proc procStruct)
 {
diff --git a/src/pl/plpython/plpy_planobject.c b/src/pl/plpython/plpy_planobject.c
index 5951d2a6ff5..ec2439c6a1f 100644
--- a/src/pl/plpython/plpy_planobject.c
+++ b/src/pl/plpython/plpy_planobject.c
@@ -119,7 +119,7 @@ PLy_plan_status(PyObject *self, PyObject *args)
 	{
 		Py_INCREF(Py_True);
 		return Py_True;
-		/* return PyInt_FromLong(self->status); */
+		/* return PyLong_FromLong(self->status); */
 	}
 	return NULL;
 }
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
index 907f89d1535..1ec8ae7e87b 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -107,7 +107,6 @@ static PyMethodDef PLy_exc_methods[] = {
 	{NULL, NULL, 0, NULL}
 };
 
-#if PY_MAJOR_VERSION >= 3
 static PyModuleDef PLy_module = {
 	PyModuleDef_HEAD_INIT,
 	.m_name = "plpy",
@@ -139,7 +138,6 @@ PyInit_plpy(void)
 
 	return m;
 }
-#endif							/* PY_MAJOR_VERSION >= 3 */
 
 void
 PLy_init_plpy(void)
@@ -148,10 +146,6 @@ PLy_init_plpy(void)
 			   *main_dict,
 			   *plpy_mod;
 
-#if PY_MAJOR_VERSION < 3
-	PyObject   *plpy;
-#endif
-
 	/*
 	 * initialize plpy module
 	 */
@@ -160,13 +154,7 @@ PLy_init_plpy(void)
 	PLy_subtransaction_init_type();
 	PLy_cursor_init_type();
 
-#if PY_MAJOR_VERSION >= 3
 	PyModule_Create(&PLy_module);
-	/* for Python 3 we initialized the exceptions in PyInit_plpy */
-#else
-	plpy = Py_InitModule("plpy", PLy_methods);
-	PLy_add_exceptions(plpy);
-#endif
 
 	/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
 
@@ -189,11 +177,7 @@ PLy_add_exceptions(PyObject *plpy)
 	PyObject   *excmod;
 	HASHCTL		hash_ctl;
 
-#if PY_MAJOR_VERSION < 3
-	excmod = Py_InitModule("spiexceptions", PLy_exc_methods);
-#else
 	excmod = PyModule_Create(&PLy_exc_module);
-#endif
 	if (excmod == NULL)
 		PLy_elog(ERROR, "could not create the spiexceptions module");
 
@@ -268,7 +252,7 @@ PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
 		if (dict == NULL)
 			PLy_elog(ERROR, NULL);
 
-		sqlstate = PyString_FromString(unpack_sql_state(exception_map[i].sqlstate));
+		sqlstate = PyUnicode_FromString(unpack_sql_state(exception_map[i].sqlstate));
 		if (sqlstate == NULL)
 			PLy_elog(ERROR, "could not generate SPI exceptions");
 
@@ -346,7 +330,7 @@ PLy_quote_literal(PyObject *self, PyObject *args)
 		return NULL;
 
 	quoted = quote_literal_cstr(str);
-	ret = PyString_FromString(quoted);
+	ret = PyUnicode_FromString(quoted);
 	pfree(quoted);
 
 	return ret;
@@ -363,10 +347,10 @@ PLy_quote_nullable(PyObject *self, PyObject *args)
 		return NULL;
 
 	if (str == NULL)
-		return PyString_FromString("NULL");
+		return PyUnicode_FromString("NULL");
 
 	quoted = quote_literal_cstr(str);
-	ret = PyString_FromString(quoted);
+	ret = PyUnicode_FromString(quoted);
 	pfree(quoted);
 
 	return ret;
@@ -383,7 +367,7 @@ PLy_quote_ident(PyObject *self, PyObject *args)
 		return NULL;
 
 	quoted = quote_identifier(str);
-	ret = PyString_FromString(quoted);
+	ret = PyUnicode_FromString(quoted);
 
 	return ret;
 }
@@ -400,7 +384,7 @@ object_to_string(PyObject *obj)
 		{
 			char	   *str;
 
-			str = pstrdup(PyString_AsString(so));
+			str = pstrdup(PLyUnicode_AsString(so));
 			Py_DECREF(so);
 
 			return str;
@@ -444,7 +428,7 @@ PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
 	else
 		so = PyObject_Str(args);
 
-	if (so == NULL || ((message = PyString_AsString(so)) == NULL))
+	if (so == NULL || ((message = PLyUnicode_AsString(so)) == NULL))
 	{
 		level = ERROR;
 		message = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
@@ -457,7 +441,7 @@ PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
 	{
 		while (PyDict_Next(kw, &pos, &key, &value))
 		{
-			char	   *keyword = PyString_AsString(key);
+			char	   *keyword = PLyUnicode_AsString(key);
 
 			if (strcmp(keyword, "message") == 0)
 			{
diff --git a/src/pl/plpython/plpy_plpymodule.h b/src/pl/plpython/plpy_plpymodule.h
index 54d78101ceb..ad6436aca78 100644
--- a/src/pl/plpython/plpy_plpymodule.h
+++ b/src/pl/plpython/plpy_plpymodule.h
@@ -11,9 +11,7 @@
 extern HTAB *PLy_spi_exceptions;
 
 
-#if PY_MAJOR_VERSION >= 3
 PyMODINIT_FUNC PyInit_plpy(void);
-#endif
 extern void PLy_init_plpy(void);
 
 #endif							/* PLPY_PLPYMODULE_H */
diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c
index 54f39419c84..f72efe81d7c 100644
--- a/src/pl/plpython/plpy_resultobject.c
+++ b/src/pl/plpython/plpy_resultobject.c
@@ -76,7 +76,7 @@ PLy_result_new(void)
 
 	Py_INCREF(Py_None);
 	ob->status = Py_None;
-	ob->nrows = PyInt_FromLong(-1);
+	ob->nrows = PyLong_FromLong(-1);
 	ob->rows = PyList_New(0);
 	ob->tupdesc = NULL;
 	if (!ob->rows)
@@ -125,7 +125,7 @@ PLy_result_colnames(PyObject *self, PyObject *unused)
 	{
 		Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
 
-		PyList_SET_ITEM(list, i, PyString_FromString(NameStr(attr->attname)));
+		PyList_SET_ITEM(list, i, PyUnicode_FromString(NameStr(attr->attname)));
 	}
 
 	return list;
@@ -151,7 +151,7 @@ PLy_result_coltypes(PyObject *self, PyObject *unused)
 	{
 		Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
 
-		PyList_SET_ITEM(list, i, PyInt_FromLong(attr->atttypid));
+		PyList_SET_ITEM(list, i, PyLong_FromLong(attr->atttypid));
 	}
 
 	return list;
@@ -177,7 +177,7 @@ PLy_result_coltypmods(PyObject *self, PyObject *unused)
 	{
 		Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
 
-		PyList_SET_ITEM(list, i, PyInt_FromLong(attr->atttypmod));
+		PyList_SET_ITEM(list, i, PyLong_FromLong(attr->atttypmod));
 	}
 
 	return list;
@@ -226,19 +226,11 @@ PLy_result_str(PyObject *arg)
 {
 	PLyResultObject *ob = (PLyResultObject *) arg;
 
-#if PY_MAJOR_VERSION >= 3
 	return PyUnicode_FromFormat("<%s status=%S nrows=%S rows=%S>",
 								Py_TYPE(ob)->tp_name,
 								ob->status,
 								ob->nrows,
 								ob->rows);
-#else
-	return PyString_FromFormat("<%s status=%ld nrows=%ld rows=%s>",
-							   ob->ob_type->tp_name,
-							   PyInt_AsLong(ob->status),
-							   PyInt_AsLong(ob->nrows),
-							   PyString_AsString(PyObject_Str(ob->rows)));
-#endif
 }
 
 static PyObject *
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 86d70470a74..9a71a42c15f 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -90,9 +90,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
 			int32		typmod;
 
 			optr = PySequence_GetItem(list, i);
-			if (PyString_Check(optr))
-				sptr = PyString_AsString(optr);
-			else if (PyUnicode_Check(optr))
+			if (PyUnicode_Check(optr))
 				sptr = PLyUnicode_AsString(optr);
 			else
 			{
@@ -186,7 +184,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
 
 	if (list != NULL)
 	{
-		if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
+		if (!PySequence_Check(list) || PyUnicode_Check(list))
 		{
 			PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
 			return NULL;
@@ -205,7 +203,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
 
 		if (!so)
 			PLy_elog(ERROR, "could not execute plan");
-		sv = PyString_AsString(so);
+		sv = PLyUnicode_AsString(so);
 		PLy_exception_set_plural(PyExc_TypeError,
 								 "Expected sequence of %d argument, got %d: %s",
 								 "Expected sequence of %d arguments, got %d: %s",
@@ -360,7 +358,7 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
 		return NULL;
 	}
 	Py_DECREF(result->status);
-	result->status = PyInt_FromLong(status);
+	result->status = PyLong_FromLong(status);
 
 	if (status > 0 && tuptable == NULL)
 	{
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 5e807b139f1..20e299bb10f 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -26,8 +26,8 @@ static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d);
-static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d);
-static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyLong_FromInt16(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyLong_FromInt32(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyLong_FromOid(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d);
@@ -517,10 +517,10 @@ PLy_input_setup_func(PLyDatumToOb *arg, MemoryContext arg_mcxt,
 				arg->func = PLyDecimal_FromNumeric;
 				break;
 			case INT2OID:
-				arg->func = PLyInt_FromInt16;
+				arg->func = PLyLong_FromInt16;
 				break;
 			case INT4OID:
-				arg->func = PLyInt_FromInt32;
+				arg->func = PLyLong_FromInt32;
 				break;
 			case INT8OID:
 				arg->func = PLyLong_FromInt64;
@@ -600,15 +600,15 @@ PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d)
 }
 
 static PyObject *
-PLyInt_FromInt16(PLyDatumToOb *arg, Datum d)
+PLyLong_FromInt16(PLyDatumToOb *arg, Datum d)
 {
-	return PyInt_FromLong(DatumGetInt16(d));
+	return PyLong_FromLong(DatumGetInt16(d));
 }
 
 static PyObject *
-PLyInt_FromInt32(PLyDatumToOb *arg, Datum d)
+PLyLong_FromInt32(PLyDatumToOb *arg, Datum d)
 {
-	return PyInt_FromLong(DatumGetInt32(d));
+	return PyLong_FromLong(DatumGetInt32(d));
 }
 
 static PyObject *
@@ -641,7 +641,7 @@ static PyObject *
 PLyString_FromScalar(PLyDatumToOb *arg, Datum d)
 {
 	char	   *x = OutputFunctionCall(&arg->u.scalar.typfunc, d);
-	PyObject   *r = PyString_FromString(x);
+	PyObject   *r = PyUnicode_FromString(x);
 
 	pfree(x);
 	return r;
@@ -954,7 +954,7 @@ PLyObject_ToComposite(PLyObToDatum *arg, PyObject *plrv,
 	 * The string conversion case doesn't require a tupdesc, nor per-field
 	 * conversion data, so just go for it if that's the case to use.
 	 */
-	if (PyString_Check(plrv) || PyUnicode_Check(plrv))
+	if (PyUnicode_Check(plrv))
 		return PLyString_ToComposite(arg, plrv, inarray);
 
 	/*
@@ -1032,25 +1032,17 @@ PLyObject_AsString(PyObject *plrv)
 	else if (PyFloat_Check(plrv))
 	{
 		/* use repr() for floats, str() is lossy */
-#if PY_MAJOR_VERSION >= 3
 		PyObject   *s = PyObject_Repr(plrv);
 
 		plrv_bo = PLyUnicode_Bytes(s);
 		Py_XDECREF(s);
-#else
-		plrv_bo = PyObject_Repr(plrv);
-#endif
 	}
 	else
 	{
-#if PY_MAJOR_VERSION >= 3
 		PyObject   *s = PyObject_Str(plrv);
 
 		plrv_bo = PLyUnicode_Bytes(s);
 		Py_XDECREF(s);
-#else
-		plrv_bo = PyObject_Str(plrv);
-#endif
 	}
 	if (!plrv_bo)
 		PLy_elog(ERROR, "could not create string representation of Python object");
diff --git a/src/pl/plpython/plpy_util.c b/src/pl/plpython/plpy_util.c
index 4a7d7264d79..22e2a599ad9 100644
--- a/src/pl/plpython/plpy_util.c
+++ b/src/pl/plpython/plpy_util.c
@@ -78,12 +78,6 @@ PLyUnicode_Bytes(PyObject *unicode)
  * Convert a Python unicode object to a C string in PostgreSQL server
  * encoding.  No Python object reference is passed out of this
  * function.  The result is palloc'ed.
- *
- * Note that this function is disguised as PyString_AsString() when
- * using Python 3.  That function returns a pointer into the internal
- * memory of the argument, which isn't exactly the interface of this
- * function.  But in either case you get a rather short-lived
- * reference that you ought to better leave alone.
  */
 char *
 PLyUnicode_AsString(PyObject *unicode)
@@ -95,7 +89,6 @@ PLyUnicode_AsString(PyObject *unicode)
 	return rv;
 }
 
-#if PY_MAJOR_VERSION >= 3
 /*
  * Convert a C string in the PostgreSQL server encoding to a Python
  * unicode object.  Reference ownership is passed to the caller.
@@ -126,5 +119,3 @@ PLyUnicode_FromString(const char *s)
 {
 	return PLyUnicode_FromStringAndSize(s, strlen(s));
 }
-
-#endif							/* PY_MAJOR_VERSION >= 3 */
diff --git a/src/pl/plpython/plpy_util.h b/src/pl/plpython/plpy_util.h
index c9ba7edc0ec..7c6577925ea 100644
--- a/src/pl/plpython/plpy_util.h
+++ b/src/pl/plpython/plpy_util.h
@@ -11,9 +11,7 @@
 extern PyObject *PLyUnicode_Bytes(PyObject *unicode);
 extern char *PLyUnicode_AsString(PyObject *unicode);
 
-#if PY_MAJOR_VERSION >= 3
 extern PyObject *PLyUnicode_FromString(const char *s);
 extern PyObject *PLyUnicode_FromStringAndSize(const char *s, Py_ssize_t size);
-#endif
 
 #endif							/* PLPY_UTIL_H */
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 05e4362dab9..2a0c9bf0361 100644
--- a/src/pl/plpython/plpython.h
+++ b/src/pl/plpython/plpython.h
@@ -59,37 +59,6 @@
 #include <Python.h>
 #endif
 
-/*
- * Python 2/3 strings/unicode/bytes handling.  Python 2 has strings
- * and unicode, Python 3 has strings, which are unicode on the C
- * level, and bytes.  The porting convention, which is similarly used
- * in Python 2.6, is that "Unicode" is always unicode, and "Bytes" are
- * bytes in Python 3 and strings in Python 2.  Since we keep
- * supporting Python 2 and its usual strings, we provide a
- * compatibility layer for Python 3 that when asked to convert a C
- * string to a Python string it converts the C string from the
- * PostgreSQL server encoding to a Python Unicode object.
- */
-#if PY_MAJOR_VERSION >= 3
-#define PyString_Check(x) 0
-#define PyString_AsString(x) PLyUnicode_AsString(x)
-#define PyString_FromString(x) PLyUnicode_FromString(x)
-#define PyString_FromStringAndSize(x, size) PLyUnicode_FromStringAndSize(x, size)
-#endif
-
-/*
- * Python 3 only has long.
- */
-#if PY_MAJOR_VERSION >= 3
-#define PyInt_FromLong(x) PyLong_FromLong(x)
-#define PyInt_AsLong(x) PyLong_AsLong(x)
-#endif
-
-/* Python 3 removed the Py_TPFLAGS_HAVE_ITER flag */
-#if PY_MAJOR_VERSION >= 3
-#define Py_TPFLAGS_HAVE_ITER 0
-#endif
-
 /* define our text domain for translations */
 #undef TEXTDOMAIN
 #define TEXTDOMAIN PG_TEXTDOMAIN("plpython")
@@ -130,8 +99,7 @@
 #define printf(...)		pg_printf(__VA_ARGS__)
 
 /*
- * Used throughout, and also by the Python 2/3 porting layer, so it's easier to
- * just include it everywhere.
+ * Used throughout, so it's easier to just include it everywhere.
  */
 #include "plpy_util.h"
 
diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c
index 39bad558023..9be45b7065b 100644
--- a/contrib/hstore_plpython/hstore_plpython.c
+++ b/contrib/hstore_plpython/hstore_plpython.c
@@ -12,10 +12,8 @@ extern void _PG_init(void);
 /* Linkage to functions in plpython module */
 typedef char *(*PLyObject_AsString_t) (PyObject *plrv);
 static PLyObject_AsString_t PLyObject_AsString_p;
-#if PY_MAJOR_VERSION >= 3
 typedef PyObject *(*PLyUnicode_FromStringAndSize_t) (const char *s, Py_ssize_t size);
 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
-#endif
 
 /* Linkage to functions in hstore module */
 typedef HStore *(*hstoreUpgrade_t) (Datum orig);
@@ -41,12 +39,10 @@ _PG_init(void)
 	PLyObject_AsString_p = (PLyObject_AsString_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
 							   true, NULL);
-#if PY_MAJOR_VERSION >= 3
 	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-#endif
 	AssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
 	hstoreUpgrade_p = (hstoreUpgrade_t)
 		load_external_function("$libdir/hstore", "hstoreUpgrade",
@@ -102,16 +98,16 @@ hstore_to_plpython(PG_FUNCTION_ARGS)
 	{
 		PyObject   *key;
 
-		key = PyString_FromStringAndSize(HSTORE_KEY(entries, base, i),
-										 HSTORE_KEYLEN(entries, i));
+		key = PyUnicode_FromStringAndSize(HSTORE_KEY(entries, base, i),
+										  HSTORE_KEYLEN(entries, i));
 		if (HSTORE_VALISNULL(entries, i))
 			PyDict_SetItem(dict, key, Py_None);
 		else
 		{
 			PyObject   *value;
 
-			value = PyString_FromStringAndSize(HSTORE_VAL(entries, base, i),
-											   HSTORE_VALLEN(entries, i));
+			value = PyUnicode_FromStringAndSize(HSTORE_VAL(entries, base, i),
+												HSTORE_VALLEN(entries, i));
 			PyDict_SetItem(dict, key, value);
 			Py_XDECREF(value);
 		}
diff --git a/contrib/jsonb_plpython/jsonb_plpython.c b/contrib/jsonb_plpython/jsonb_plpython.c
index 836c1787706..da0e343b519 100644
--- a/contrib/jsonb_plpython/jsonb_plpython.c
+++ b/contrib/jsonb_plpython/jsonb_plpython.c
@@ -28,11 +28,9 @@ static PyObject *PLyObject_FromJsonbContainer(JsonbContainer *jsonb);
 static JsonbValue *PLyObject_ToJsonbValue(PyObject *obj,
 										  JsonbParseState **jsonb_state, bool is_elem);
 
-#if PY_MAJOR_VERSION >= 3
 typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
 			(const char *s, Py_ssize_t size);
 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
-#endif
 
 /*
  * Module initialize function: fetch function pointers for cross-module calls.
@@ -45,13 +43,10 @@ _PG_init(void)
 	PLyObject_AsString_p = (PLyObject_AsString_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
 							   true, NULL);
-#if PY_MAJOR_VERSION >= 3
 	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-#endif
-
 	AssertVariableIsOfType(&PLy_elog_impl, PLy_elog_impl_t);
 	PLy_elog_impl_p = (PLy_elog_impl_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLy_elog_impl",
@@ -74,7 +69,7 @@ PLyString_FromJsonbValue(JsonbValue *jbv)
 {
 	Assert(jbv->type == jbvString);
 
-	return PyString_FromStringAndSize(jbv->val.string.val, jbv->val.string.len);
+	return PyUnicode_FromStringAndSize(jbv->val.string.val, jbv->val.string.len);
 }
 
 /*
@@ -415,7 +410,7 @@ PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_ele
 {
 	JsonbValue *out;
 
-	if (!(PyString_Check(obj) || PyUnicode_Check(obj)))
+	if (!PyUnicode_Check(obj))
 	{
 		if (PySequence_Check(obj))
 			return PLySequence_ToJsonbValue(obj, jsonb_state);
@@ -427,7 +422,7 @@ PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_ele
 
 	if (obj == Py_None)
 		out->type = jbvNull;
-	else if (PyString_Check(obj) || PyUnicode_Check(obj))
+	else if (PyUnicode_Check(obj))
 		PLyString_ToJsonbValue(obj, out);
 
 	/*
diff --git a/contrib/ltree_plpython/ltree_plpython.c b/contrib/ltree_plpython/ltree_plpython.c
index 1570e77dd9f..459b2be6b0d 100644
--- a/contrib/ltree_plpython/ltree_plpython.c
+++ b/contrib/ltree_plpython/ltree_plpython.c
@@ -9,10 +9,8 @@ PG_MODULE_MAGIC;
 extern void _PG_init(void);
 
 /* Linkage to functions in plpython module */
-#if PY_MAJOR_VERSION >= 3
 typedef PyObject *(*PLyUnicode_FromStringAndSize_t) (const char *s, Py_ssize_t size);
 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
-#endif
 
 
 /*
@@ -22,12 +20,10 @@ void
 _PG_init(void)
 {
 	/* Asserts verify that typedefs above match original declarations */
-#if PY_MAJOR_VERSION >= 3
 	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-#endif
 }
 
 
@@ -54,7 +50,7 @@ ltree_to_plpython(PG_FUNCTION_ARGS)
 	curlevel = LTREE_FIRST(in);
 	for (i = 0; i < in->numlevel; i++)
 	{
-		PyList_SetItem(list, i, PyString_FromStringAndSize(curlevel->name, curlevel->len));
+		PyList_SetItem(list, i, PyUnicode_FromStringAndSize(curlevel->name, curlevel->len));
 		curlevel = LEVEL_NEXT(curlevel);
 	}
 
-- 
2.35.1.354.g715d08a9e5

v6-0004-plpython-Adjust-docs-after-removal-of-Python-2-su.patchtext/x-diff; charset=us-asciiDownload
From 781079a3f755b33e36505bd98cdff209427614c1 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 6 Mar 2022 16:57:28 -0800
Subject: [PATCH v6 4/4] plpython: Adjust docs after removal of Python 2
 support.

Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 doc/src/sgml/hstore.sgml               |   8 +-
 doc/src/sgml/json.sgml                 |   9 +-
 doc/src/sgml/ltree.sgml                |   8 +-
 doc/src/sgml/plpython.sgml             | 275 +++++--------------------
 doc/src/sgml/ref/comment.sgml          |   2 +-
 doc/src/sgml/ref/create_transform.sgml |   6 +-
 doc/src/sgml/ref/drop_transform.sgml   |   4 +-
 7 files changed, 63 insertions(+), 249 deletions(-)

diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 870063c288b..679878b3afc 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -943,12 +943,8 @@ ALTER TABLE tablename ALTER hstorecol TYPE hstore USING hstorecol || '';
    and <literal>hstore_plperlu</literal>, for trusted and untrusted PL/Perl.
    If you install these transforms and specify them when creating a
    function, <type>hstore</type> values are mapped to Perl hashes.  The
-   extensions for PL/Python are
-   called <literal>hstore_plpythonu</literal>, <literal>hstore_plpython2u</literal>,
-   and <literal>hstore_plpython3u</literal>
-   (see <xref linkend="plpython-python23"/> for the PL/Python naming
-   convention).  If you use them, <type>hstore</type> values are mapped to
-   Python dictionaries.
+   extension for PL/Python is called <literal>hstore_plpython3u</literal>.
+   If you use it, <type>hstore</type> values are mapped to Python dictionaries.
   </para>
 
   <caution>
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 673c70c3bb8..b3b16b8a77e 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -716,12 +716,9 @@ UPDATE table_name SET jsonb_field[1]['a'] = '1';
   </para>
 
   <para>
-   The extensions for PL/Python are called <literal>jsonb_plpythonu</literal>,
-   <literal>jsonb_plpython2u</literal>, and
-   <literal>jsonb_plpython3u</literal> (see <xref
-   linkend="plpython-python23"/> for the PL/Python naming convention).  If you
-   use them, <type>jsonb</type> values are mapped to Python dictionaries,
-   lists, and scalars, as appropriate.
+   The extensions for PL/Python is called <literal>jsonb_plpython3u</literal>.
+   If you use it, <type>jsonb</type> values are mapped to Python
+   dictionaries, lists, and scalars, as appropriate.
   </para>
 
   <para>
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index 436be76bfaa..4ff384c7ee7 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -826,12 +826,8 @@ ltreetest=&gt; SELECT ins_label(path,2,'Space') FROM test WHERE path &lt;@ 'Top.
   <title>Transforms</title>
 
   <para>
-   Additional extensions are available that implement transforms for
-   the <type>ltree</type> type for PL/Python.  The extensions are
-   called <literal>ltree_plpythonu</literal>, <literal>ltree_plpython2u</literal>,
-   and <literal>ltree_plpython3u</literal>
-   (see <xref linkend="plpython-python23"/> for the PL/Python naming
-   convention).  If you install these transforms and specify them when
+   The <literal>ltree_plpython3u</literal> implement transforms for the
+   <type>ltree</type> type for PL/Python. If installed and specified when
    creating a function, <type>ltree</type> values are mapped to Python lists.
    (The reverse is currently not supported, however.)
   </para>
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index b67f8f4aaed..fab4330cd1d 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -14,8 +14,7 @@
 
  <para>
   To install PL/Python in a particular database, use
-  <literal>CREATE EXTENSION plpythonu</literal> (but
-  see also <xref linkend="plpython-python23"/>).
+  <literal>CREATE EXTENSION plpython3u</literal>.
  </para>
 
   <tip>
@@ -28,14 +27,14 @@
  <para>
   PL/Python is only available as an <quote>untrusted</quote> language, meaning
   it does not offer any way of restricting what users can do in it and
-  is therefore named <literal>plpythonu</literal>.  A trusted
+  is therefore named <literal>plpython3u</literal>.  A trusted
   variant <literal>plpython</literal> might become available in the future
   if a secure execution mechanism is developed in Python.  The
   writer of a function in untrusted PL/Python must take care that the
   function cannot be used to do anything unwanted, since it will be
   able to do anything that could be done by a user logged in as the
   database administrator.  Only superusers can create functions in
-  untrusted languages such as <literal>plpythonu</literal>.
+  untrusted languages such as <literal>plpython3u</literal>.
  </para>
 
  <note>
@@ -51,133 +50,10 @@
   <title>Python 2 vs. Python 3</title>
 
   <para>
-   PL/Python supports both the Python 2 and Python 3 language
-   variants.  (The PostgreSQL installation instructions might contain
-   more precise information about the exact supported minor versions
-   of Python.)  Because the Python 2 and Python 3 language variants
-   are incompatible in some important aspects, the following naming
-   and transitioning scheme is used by PL/Python to avoid mixing them:
-
-   <itemizedlist>
-    <listitem>
-     <para>
-      The PostgreSQL language named <literal>plpython2u</literal>
-      implements PL/Python based on the Python 2 language variant.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The PostgreSQL language named <literal>plpython3u</literal>
-      implements PL/Python based on the Python 3 language variant.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The language named <literal>plpythonu</literal> implements
-      PL/Python based on the default Python language variant, which is
-      currently Python 2.  (This default is independent of what any
-      local Python installations might consider to be
-      their <quote>default</quote>, for example,
-      what <filename>/usr/bin/python</filename> might be.)  The
-      default will probably be changed to Python 3 in a distant future
-      release of PostgreSQL, depending on the progress of the
-      migration to Python 3 in the Python community.
-     </para>
-    </listitem>
-   </itemizedlist>
-
-   This scheme is analogous to the recommendations in <ulink
-   url="https://www.python.org/dev/peps/pep-0394/">PEP 394</ulink> regarding the
-   naming and transitioning of the <command>python</command> command.
-  </para>
-
-  <para>
-   It depends on the build configuration or the installed packages
-   whether PL/Python for Python 2 or Python 3 or both are available.
-  </para>
-
-  <tip>
-   <para>
-    The built variant depends on which Python version was found during
-    the installation or which version was explicitly set using
-    the <envar>PYTHON</envar> environment variable;
-    see <xref linkend="install-procedure"/>.  To make both variants of
-    PL/Python available in one installation, the source tree has to be
-    configured and built twice.
-   </para>
-  </tip>
-
-  <para>
-   This results in the following usage and migration strategy:
-
-   <itemizedlist>
-    <listitem>
-     <para>
-      Existing users and users who are currently not interested in
-      Python 3 use the language name <literal>plpythonu</literal> and
-      don't have to change anything for the foreseeable future.  It is
-      recommended to gradually <quote>future-proof</quote> the code
-      via migration to Python 2.6/2.7 to simplify the eventual
-      migration to Python 3.
-     </para>
-
-     <para>
-      In practice, many PL/Python functions will migrate to Python 3
-      with few or no changes.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      Users who know that they have heavily Python 2 dependent code
-      and don't plan to ever change it can make use of
-      the <literal>plpython2u</literal> language name.  This will
-      continue to work into the very distant future, until Python 2
-      support might be completely dropped by PostgreSQL.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      Users who want to dive into Python 3 can use
-      the <literal>plpython3u</literal> language name, which will keep
-      working forever by today's standards.  In the distant future,
-      when Python 3 might become the default, they might like to
-      remove the <quote>3</quote> for aesthetic reasons.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      Daredevils, who want to build a Python-3-only operating system
-      environment, can change the contents of
-      <literal>plpythonu</literal>'s extension control and script files
-      to make <literal>plpythonu</literal> be equivalent
-      to <literal>plpython3u</literal>, keeping in mind that this
-      would make their installation incompatible with most of the rest
-      of the world.
-     </para>
-    </listitem>
-   </itemizedlist>
-  </para>
-
-  <para>
-   See also the
-   document <ulink url="https://docs.python.org/3/whatsnew/3.0.html">What's
-   New In Python 3.0</ulink> for more information about porting to
-   Python 3.
-  </para>
-
-  <para>
-   It is not allowed to use PL/Python based on Python 2 and PL/Python
-   based on Python 3 in the same session, because the symbols in the
-   dynamic modules would clash, which could result in crashes of the
-   PostgreSQL server process.  There is a check that prevents mixing
-   Python major versions in a session, which will abort the session if
-   a mismatch is detected.  It is possible, however, to use both
-   PL/Python variants in the same database, from separate sessions.
+   PL/Python supports only Python 3. Past versions of
+   <productname>PostgreSQL</productname> supported Python 2, using the
+   <literal>plpyhonu</literal> and <literal>plpython2u</literal> language
+   names.
   </para>
  </sect1>
 
@@ -193,7 +69,7 @@ CREATE FUNCTION <replaceable>funcname</replaceable> (<replaceable>argument-list<
   RETURNS <replaceable>return-type</replaceable>
 AS $$
   # PL/Python function body
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
   </para>
 
@@ -225,7 +101,7 @@ AS $$
   if a &gt; b:
     return a
   return b
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
    The Python code that is given as the body of the function definition
@@ -255,7 +131,7 @@ CREATE FUNCTION pystrip(x text)
 AS $$
   x = x.strip()  # error
   return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
    because assigning to <varname>x</varname>
    makes <varname>x</varname> a local variable for the entire block,
@@ -271,7 +147,7 @@ AS $$
   global x
   x = x.strip()  # ok now
   return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
    But it is advisable not to rely on this implementation detail of
    PL/Python.  It is better to treat the function parameters as
@@ -303,11 +179,8 @@ $$ LANGUAGE plpythonu;
 
      <listitem>
       <para>
-       PostgreSQL <type>smallint</type> and <type>int</type> are
-       converted to Python <type>int</type>.
-       PostgreSQL <type>bigint</type> and <type>oid</type> are converted
-       to <type>long</type> in Python 2 and to <type>int</type> in
-       Python 3.
+       PostgreSQL <type>smallint</type>, <type>int</type>, <type>bigint</type>
+       and <type>oid</type> are converted to Python <type>int</type>.
       </para>
      </listitem>
 
@@ -335,19 +208,15 @@ $$ LANGUAGE plpythonu;
 
      <listitem>
       <para>
-       PostgreSQL <type>bytea</type> is converted to
-       Python <type>str</type> in Python 2 and to <type>bytes</type>
-       in Python 3.  In Python 2, the string should be treated as a
-       byte sequence without any character encoding.
+       PostgreSQL <type>bytea</type> is converted to Python <type>bytes</type>.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       All other data types, including the PostgreSQL character string
-       types, are converted to a Python <type>str</type>.  In Python
-       2, this string will be in the PostgreSQL server encoding; in
-       Python 3, it will be a Unicode string like all strings.
+       All other data types, including the PostgreSQL character string types,
+       are converted to a Python <type>str</type> (in Unicode like all Python
+       strings).
       </para>
      </listitem>
 
@@ -375,10 +244,10 @@ $$ LANGUAGE plpythonu;
 
      <listitem>
       <para>
-       When the PostgreSQL return type is <type>bytea</type>, the
-       return value will be converted to a string (Python 2) or bytes
-       (Python 3) using the respective Python built-ins, with the
-       result being converted to <type>bytea</type>.
+       When the PostgreSQL return type is <type>bytea</type>, the return value
+       will be converted to Python <type>bytes</type> using the respective
+       Python built-ins, with the result being converted to
+       <type>bytea</type>.
       </para>
      </listitem>
 
@@ -393,14 +262,8 @@ $$ LANGUAGE plpythonu;
       </para>
 
       <para>
-       Strings in Python 2 are required to be in the PostgreSQL server
-       encoding when they are passed to PostgreSQL.  Strings that are
-       not valid in the current server encoding will raise an error,
-       but not all encoding mismatches can be detected, so garbage
-       data can still result when this is not done correctly.  Unicode
-       strings are converted to the correct encoding automatically, so
-       it can be safer and more convenient to use those.  In Python 3,
-       all strings are Unicode strings.
+       Strings are automatically converted to the PostgreSQL server encoding
+       when they are passed to PostgreSQL.
       </para>
      </listitem>
 
@@ -440,7 +303,7 @@ AS $$
   if a &gt; b:
     return a
   return b
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
    As shown above, to return an SQL null value from a PL/Python
@@ -461,10 +324,10 @@ CREATE FUNCTION return_arr()
   RETURNS int[]
 AS $$
 return [1, 2, 3, 4, 5]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT return_arr();
- return_arr  
+ return_arr
 -------------
  {1,2,3,4,5}
 (1 row)
@@ -479,11 +342,11 @@ SELECT return_arr();
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
 INFO:  ([[1, 2, 3], [4, 5, 6]], &lt;type 'list'&gt;)
- test_type_conversion_array_int4 
+ test_type_conversion_array_int4
 ---------------------------------
  {{1,2,3},{4,5,6}}
 (1 row)
@@ -506,7 +369,7 @@ CREATE FUNCTION return_str_arr()
   RETURNS varchar[]
 AS $$
 return "hello"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT return_str_arr();
  return_str_arr
@@ -540,7 +403,7 @@ AS $$
   if (e["age"] &lt; 30) and (e["salary"] &gt; 100000):
     return True
   return False
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
   </para>
 
@@ -574,7 +437,7 @@ CREATE FUNCTION make_pair (name text, value integer)
 AS $$
   return ( name, value )
   # or alternatively, as list: return [ name, value ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
        To return an SQL null for any column, insert <symbol>None</symbol> at
@@ -600,7 +463,7 @@ CREATE FUNCTION make_pair (name text, value integer)
   RETURNS named_value
 AS $$
   return { "name": name, "value": value }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
        Any extra dictionary key/value pairs are ignored. Missing keys are
@@ -633,7 +496,7 @@ AS $$
   nv.name = name
   nv.value = value
   return nv
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
       </para>
      </listitem>
@@ -646,7 +509,7 @@ $$ LANGUAGE plpythonu;
 <programlisting>
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_simple();
 </programlisting>
@@ -657,7 +520,7 @@ SELECT * FROM multiout_simple();
 <programlisting>
 CREATE PROCEDURE python_triple(INOUT a integer, INOUT b integer) AS $$
 return (a * 3, b * 3)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CALL python_triple(5, 10);
 </programlisting>
@@ -693,7 +556,7 @@ AS $$
   # return tuple containing lists as composite types
   # all other combinations work also
   return ( [ how, "World" ], [ how, "PostgreSQL" ], [ how, "PL/Python" ] )
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
       </para>
      </listitem>
@@ -724,7 +587,7 @@ AS $$
       return ( self.how, self.who[self.ndx] )
 
   return producer(how, [ "World", "PostgreSQL", "PL/Python" ])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
       </para>
      </listitem>
@@ -740,7 +603,7 @@ CREATE FUNCTION greet (how text)
 AS $$
   for who in [ "World", "PostgreSQL", "PL/Python" ]:
     yield ( how, who )
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
       </para>
@@ -756,7 +619,7 @@ $$ LANGUAGE plpythonu;
 <programlisting>
 CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_simple_setof(3);
 </programlisting>
@@ -794,7 +657,7 @@ SELECT * FROM multiout_simple_setof(3);
 <programlisting>
 DO $$
     # PL/Python code
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
    An anonymous code block receives no arguments, and whatever value it
@@ -1089,7 +952,7 @@ CREATE FUNCTION usesavedplan() RETURNS trigger AS $$
         plan = plpy.prepare("SELECT 1")
         SD["plan"] = plan
     # rest of function
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
      </para>
     </listitem>
@@ -1132,7 +995,7 @@ for row in plpy.cursor("select num from largetable"):
     if row['num'] % 2:
          odd += 1
 return odd
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$
 odd = 0
@@ -1145,7 +1008,7 @@ while True:
         if row['num'] % 2:
             odd += 1
 return odd
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$
 odd = 0
@@ -1153,7 +1016,7 @@ plan = plpy.prepare("select num from largetable where num % $1 &lt;&gt; 0", ["in
 rows = list(plpy.cursor(plan, [2]))  # or: = list(plan.cursor([2]))
 
 return len(rows)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
      </para>
 
@@ -1198,7 +1061,7 @@ CREATE FUNCTION try_adding_joe() RETURNS text AS $$
         return "something went wrong"
     else:
         return "Joe added"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
    </para>
 
@@ -1231,7 +1094,7 @@ except plpy.SPIError as e:
     return "other error, SQLSTATE %s" % e.sqlstate
 else:
     return "fraction inserted"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
     Note that because all exceptions from
     the <literal>plpy.spiexceptions</literal> module inherit
@@ -1280,7 +1143,7 @@ else:
     result = "funds transferred correctly"
 plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
 plpy.execute(plan, [result])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
     If the second <literal>UPDATE</literal> statement results in an
     exception being raised, this function will report the error, but
@@ -1312,7 +1175,7 @@ else:
     result = "funds transferred correctly"
 plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
 plpy.execute(plan, [result])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
     Note that the use of <literal>try/catch</literal> is still
     required.  Otherwise the exception would propagate to the top of
@@ -1329,44 +1192,6 @@ $$ LANGUAGE plpythonu;
     to be rolled back.
    </para>
   </sect2>
-
-  <sect2>
-   <title>Older Python Versions</title>
-
-   <para>
-    Context managers syntax using the <literal>with</literal> keyword
-    is available by default in Python 2.6.  For compatibility with
-    older Python versions, you can call the
-    subtransaction manager's <literal>__enter__</literal> and
-    <literal>__exit__</literal> functions using the
-    <literal>enter</literal> and <literal>exit</literal> convenience
-    aliases.  The example function that transfers funds could be
-    written as:
-<programlisting>
-CREATE FUNCTION transfer_funds_old() RETURNS void AS $$
-try:
-    subxact = plpy.subtransaction()
-    subxact.enter()
-    try:
-        plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'")
-        plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'")
-    except:
-        import sys
-        subxact.exit(*sys.exc_info())
-        raise
-    else:
-        subxact.exit(None, None, None)
-except plpy.SPIError as e:
-    result = "error transferring funds: %s" % e.args
-else:
-    result = "funds transferred correctly"
-
-plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
-plpy.execute(plan, [result])
-$$ LANGUAGE plpythonu;
-</programlisting>
-   </para>
-  </sect2>
  </sect1>
 
  <sect1 id="plpython-transactions">
@@ -1389,7 +1214,7 @@ $$ LANGUAGE plpythonu;
    Here is an example:
 <programlisting>
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -1465,7 +1290,7 @@ CREATE FUNCTION raise_custom_exception() RETURNS void AS $$
 plpy.error("custom exception message",
            detail="some info about exception",
            hint="hint for users")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 =# SELECT raise_custom_exception();
 ERROR:  plpy.Error: custom exception message
diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index b12796095fb..23d9029af9c 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -349,7 +349,7 @@ COMMENT ON TEXT SEARCH CONFIGURATION my_config IS 'Special word filtering';
 COMMENT ON TEXT SEARCH DICTIONARY swedish IS 'Snowball stemmer for Swedish language';
 COMMENT ON TEXT SEARCH PARSER my_parser IS 'Splits text into words';
 COMMENT ON TEXT SEARCH TEMPLATE snowball IS 'Snowball stemmer';
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'Transform between hstore and Python dict';
+COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython3u IS 'Transform between hstore and Python dict';
 COMMENT ON TRIGGER my_trigger ON my_table IS 'Used for RI';
 COMMENT ON TYPE complex IS 'Complex number data type';
 COMMENT ON VIEW my_view IS 'View of departmental costs';
diff --git a/doc/src/sgml/ref/create_transform.sgml b/doc/src/sgml/ref/create_transform.sgml
index 3f81dc6bba2..34bdc60e130 100644
--- a/doc/src/sgml/ref/create_transform.sgml
+++ b/doc/src/sgml/ref/create_transform.sgml
@@ -156,11 +156,11 @@ CREATE [ OR REPLACE ] TRANSFORM FOR <replaceable>type_name</replaceable> LANGUAG
 
   <para>
    To create a transform for type <type>hstore</type> and language
-   <literal>plpythonu</literal>, first set up the type and the language:
+   <literal>plpython3u</literal>, first set up the type and the language:
 <programlisting>
 CREATE TYPE hstore ...;
 
-CREATE EXTENSION plpythonu;
+CREATE EXTENSION plpython3u;
 </programlisting>
    Then create the necessary functions:
 <programlisting>
@@ -174,7 +174,7 @@ AS ...;
 </programlisting>
    And finally create the transform to connect them all together:
 <programlisting>
-CREATE TRANSFORM FOR hstore LANGUAGE plpythonu (
+CREATE TRANSFORM FOR hstore LANGUAGE plpython3u (
     FROM SQL WITH FUNCTION hstore_to_plpython(internal),
     TO SQL WITH FUNCTION plpython_to_hstore(internal)
 );
diff --git a/doc/src/sgml/ref/drop_transform.sgml b/doc/src/sgml/ref/drop_transform.sgml
index d25cb51604d..544a9663d77 100644
--- a/doc/src/sgml/ref/drop_transform.sgml
+++ b/doc/src/sgml/ref/drop_transform.sgml
@@ -101,9 +101,9 @@ DROP TRANSFORM [ IF EXISTS ] FOR <replaceable>type_name</replaceable> LANGUAGE <
 
   <para>
    To drop the transform for type <type>hstore</type> and language
-   <literal>plpythonu</literal>:
+   <literal>plpython3u</literal>:
 <programlisting>
-DROP TRANSFORM FOR hstore LANGUAGE plpythonu;
+DROP TRANSFORM FOR hstore LANGUAGE plpython3u;
 </programlisting></para>
  </refsect1>
 
-- 
2.35.1.354.g715d08a9e5

#74Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#73)
Re: Time to drop plpython2?

Hi,

On 2022-03-06 17:30:15 -0800, Andres Freund wrote:

0003, the removal of code level support for Python 2, is now a good bit bigger
bigger, due to the removal of the last remnants of the Py2/3 porting layer.

Oh, huh. Something here seems to be broken, causing a crash on windows, but
not elsewhere.

https://cirrus-ci.com/task/5288088032247808
stack trace and regression.diffs:
https://api.cirrus-ci.com/v1/artifact/task/5288088032247808/crashlog/crashlog-postgres.exe_1768_2022-03-07_01-07-48-535.txt
https://api.cirrus-ci.com/v1/artifact/task/5288088032247808/log/src/pl/plpython/regression.diffs

The previous version ran successfully on windows too. So I probably screwed
something up when removing the remaining Py2/3 porting layer bits... The fact
that it fails on windows but not elsewhere perhaps suggests it's something
around the width of long?

Greetings,

Andres Freund

#75Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#73)
1 attachment(s)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

Now that the BF has stabilized, I've rebased and cleaned up the patches I'd
posted earlier. Attached for the first time is my attempt at cleaning up the
docs.

I looked through this quickly, and have a couple of nitpicks. The
PGFILEDESC value for jsonb_plpython is worded randomly differently
from those for hstore_plpython and ltree_plpython. I think we should
make it match those. I also noted a couple of typos in the docs patch.
See attached delta patch (I named it .txt in hopes of not confusing
the cfbot).

I kind of wonder if we still need "46.1. Python 2 vs. Python 3" at
all. It certainly doesn't seem like it still deserves its position
of honor as the first subsection. Perhaps move it down to be the
last subsection?

Also, grepping reveals that vcregress.pl still has two stray references to
"plpythonu". I did not touch that here, but maybe that has something
to do with the ci failure?

I did so far leave in the "major version conflict" detection stuff in
plpy_main.c - that could again be useful? I'm leaning towards removing it, I'd
hope that there's not again such a painful transition, and we have the git
history if needed.

I think we should leave it in. I foresee that somebody will want to build
plpython2u as an out-of-core extension, at least for a few years yet.
If they do, and the core language does not have its half of that guard,
it'd be bad.

regards, tom lane

Attachments:

v6-0005-fixups.txttext/x-diff; charset=us-ascii; name=v6-0005-fixups.txtDownload
diff --git a/contrib/jsonb_plpython/Makefile b/contrib/jsonb_plpython/Makefile
index e3f160510d..fea7bdfc00 100644
--- a/contrib/jsonb_plpython/Makefile
+++ b/contrib/jsonb_plpython/Makefile
@@ -4,7 +4,7 @@ MODULE_big = jsonb_plpython$(python_majorversion)
 OBJS = \
 	$(WIN32RES) \
 	jsonb_plpython.o
-PGFILEDESC = "jsonb_plpython - transform between jsonb and plpython3u"
+PGFILEDESC = "jsonb_plpython - jsonb transform for plpython"
 
 PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index b3b16b8a77..c4223fafb6 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -716,7 +716,7 @@ UPDATE table_name SET jsonb_field[1]['a'] = '1';
   </para>
 
   <para>
-   The extensions for PL/Python is called <literal>jsonb_plpython3u</literal>.
+   The extension for PL/Python is called <literal>jsonb_plpython3u</literal>.
    If you use it, <type>jsonb</type> values are mapped to Python
    dictionaries, lists, and scalars, as appropriate.
   </para>
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index 4ff384c7ee..508f404ae8 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -826,15 +826,15 @@ ltreetest=&gt; SELECT ins_label(path,2,'Space') FROM test WHERE path &lt;@ 'Top.
   <title>Transforms</title>
 
   <para>
-   The <literal>ltree_plpython3u</literal> implement transforms for the
-   <type>ltree</type> type for PL/Python. If installed and specified when
+   The <literal>ltree_plpython3u</literal> extension implements transforms for
+   the <type>ltree</type> type for PL/Python. If installed and specified when
    creating a function, <type>ltree</type> values are mapped to Python lists.
    (The reverse is currently not supported, however.)
   </para>
 
   <caution>
    <para>
-    It is strongly recommended that the transform extensions be installed in
+    It is strongly recommended that the transform extension be installed in
     the same schema as <filename>ltree</filename>.  Otherwise there are
     installation-time security hazards if a transform extension's schema
     contains objects defined by a hostile user.
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index fab4330cd1..7698d5f068 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -52,7 +52,7 @@
   <para>
    PL/Python supports only Python 3. Past versions of
    <productname>PostgreSQL</productname> supported Python 2, using the
-   <literal>plpyhonu</literal> and <literal>plpython2u</literal> language
+   <literal>plpythonu</literal> and <literal>plpython2u</literal> language
    names.
   </para>
  </sect1>
#76Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#75)
4 attachment(s)
Re: Time to drop plpython2?

Hi,

On 2022-03-07 15:05:03 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

Now that the BF has stabilized, I've rebased and cleaned up the patches I'd
posted earlier. Attached for the first time is my attempt at cleaning up the
docs.

I looked through this quickly, and have a couple of nitpicks. The
PGFILEDESC value for jsonb_plpython is worded randomly differently
from those for hstore_plpython and ltree_plpython. I think we should
make it match those.

Makes sense.

I also noted a couple of typos in the docs patch.
See attached delta patch

Thanks, incorporated.

I kind of wonder if we still need "46.1. Python 2 vs. Python 3" at
all. It certainly doesn't seem like it still deserves its position
of honor as the first subsection. Perhaps move it down to be the
last subsection?

Agreed. I think it's useful to have it, just so we have a place mentioning
plpython[2]u. I put it as the second last subsection for now, somehow seemed
to belong before the env variable list? But...

Also, grepping reveals that vcregress.pl still has two stray references to
"plpythonu".

I left that in somewhat intentionally, it seemed the cleanest way to remove
plpython2 from vcregress.pl. It's purely cosmetic afaics.

A related question is whether we want to remove $(python_majorversion)
references in the makefiles?

I did not touch that here, but maybe that has something to do with the ci
failure?

The CI failure was caused by me screwing up search and replace
:(. Accidentally replaced PyString_AsString with PyUnicode_AsString instead of
P*L*yUnicode_AsString.

Somewhat surprised at that causing problems only on windows (even valgrind
didn't, although the required suppressions might have been the cause of
that). But well, better there than nowhere...

I did so far leave in the "major version conflict" detection stuff in
plpy_main.c - that could again be useful? I'm leaning towards removing it, I'd
hope that there's not again such a painful transition, and we have the git
history if needed.

I think we should leave it in. I foresee that somebody will want to build
plpython2u as an out-of-core extension, at least for a few years yet.
If they do, and the core language does not have its half of that guard,
it'd be bad.

Makes sense. Added a comment to that effect.

I noticed a few references to PLyString in function names. Probably should
replace them with PLyUnicode. Didn't do that yet in the attached...

Greetings,

Andres Freund

Attachments:

v7-0001-plpython-Remove-plpythonu-plpython2u-and-associat.patchtext/x-diff; charset=us-asciiDownload
From f8598518aa027fa4766be287a0fba549dcf11af1 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 6 Mar 2022 16:57:28 -0800
Subject: [PATCH v7 1/4] plpython: Remove plpythonu, plpython2u and associated
 transform extensions.

Reviewed-By: Peter Eisentraut <peter@eisentraut.org>
Reviewed-By: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 src/pl/plpython/plpython2u--1.0.sql           | 17 -----------------
 src/pl/plpython/plpython2u.control            |  7 -------
 src/pl/plpython/plpythonu--1.0.sql            | 17 -----------------
 src/pl/plpython/plpythonu.control             |  7 -------
 contrib/hstore_plpython/Makefile              |  4 ++--
 .../hstore_plpython2u--1.0.sql                | 19 -------------------
 .../hstore_plpython/hstore_plpython2u.control |  6 ------
 .../hstore_plpython/hstore_plpythonu--1.0.sql | 19 -------------------
 .../hstore_plpython/hstore_plpythonu.control  |  6 ------
 contrib/jsonb_plpython/Makefile               |  6 +++---
 .../jsonb_plpython/jsonb_plpython2u--1.0.sql  | 19 -------------------
 .../jsonb_plpython/jsonb_plpython2u.control   |  6 ------
 .../jsonb_plpython/jsonb_plpythonu--1.0.sql   | 19 -------------------
 .../jsonb_plpython/jsonb_plpythonu.control    |  6 ------
 contrib/ltree_plpython/Makefile               |  4 ++--
 .../ltree_plpython/ltree_plpython2u--1.0.sql  | 12 ------------
 .../ltree_plpython/ltree_plpython2u.control   |  6 ------
 .../ltree_plpython/ltree_plpythonu--1.0.sql   | 12 ------------
 .../ltree_plpython/ltree_plpythonu.control    |  6 ------
 19 files changed, 7 insertions(+), 191 deletions(-)
 delete mode 100644 src/pl/plpython/plpython2u--1.0.sql
 delete mode 100644 src/pl/plpython/plpython2u.control
 delete mode 100644 src/pl/plpython/plpythonu--1.0.sql
 delete mode 100644 src/pl/plpython/plpythonu.control
 delete mode 100644 contrib/hstore_plpython/hstore_plpython2u--1.0.sql
 delete mode 100644 contrib/hstore_plpython/hstore_plpython2u.control
 delete mode 100644 contrib/hstore_plpython/hstore_plpythonu--1.0.sql
 delete mode 100644 contrib/hstore_plpython/hstore_plpythonu.control
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpython2u.control
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
 delete mode 100644 contrib/jsonb_plpython/jsonb_plpythonu.control
 delete mode 100644 contrib/ltree_plpython/ltree_plpython2u--1.0.sql
 delete mode 100644 contrib/ltree_plpython/ltree_plpython2u.control
 delete mode 100644 contrib/ltree_plpython/ltree_plpythonu--1.0.sql
 delete mode 100644 contrib/ltree_plpython/ltree_plpythonu.control

diff --git a/src/pl/plpython/plpython2u--1.0.sql b/src/pl/plpython/plpython2u--1.0.sql
deleted file mode 100644
index 69f74775678..00000000000
--- a/src/pl/plpython/plpython2u--1.0.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/* src/pl/plpython/plpython2u--1.0.sql */
-
-CREATE FUNCTION plpython2_call_handler() RETURNS language_handler
-  LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython2_inline_handler(internal) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython2_validator(oid) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE LANGUAGE plpython2u
-  HANDLER plpython2_call_handler
-  INLINE plpython2_inline_handler
-  VALIDATOR plpython2_validator;
-
-COMMENT ON LANGUAGE plpython2u IS 'PL/Python2U untrusted procedural language';
diff --git a/src/pl/plpython/plpython2u.control b/src/pl/plpython/plpython2u.control
deleted file mode 100644
index 39c2b791efe..00000000000
--- a/src/pl/plpython/plpython2u.control
+++ /dev/null
@@ -1,7 +0,0 @@
-# plpython2u extension
-comment = 'PL/Python2U untrusted procedural language'
-default_version = '1.0'
-module_pathname = '$libdir/plpython2'
-relocatable = false
-schema = pg_catalog
-superuser = true
diff --git a/src/pl/plpython/plpythonu--1.0.sql b/src/pl/plpython/plpythonu--1.0.sql
deleted file mode 100644
index 4c6f7c3f140..00000000000
--- a/src/pl/plpython/plpythonu--1.0.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/* src/pl/plpython/plpythonu--1.0.sql */
-
-CREATE FUNCTION plpython_call_handler() RETURNS language_handler
-  LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_inline_handler(internal) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_validator(oid) RETURNS void
-  STRICT LANGUAGE c AS 'MODULE_PATHNAME';
-
-CREATE LANGUAGE plpythonu
-  HANDLER plpython_call_handler
-  INLINE plpython_inline_handler
-  VALIDATOR plpython_validator;
-
-COMMENT ON LANGUAGE plpythonu IS 'PL/PythonU untrusted procedural language';
diff --git a/src/pl/plpython/plpythonu.control b/src/pl/plpython/plpythonu.control
deleted file mode 100644
index ae91b1c255c..00000000000
--- a/src/pl/plpython/plpythonu.control
+++ /dev/null
@@ -1,7 +0,0 @@
-# plpythonu extension
-comment = 'PL/PythonU untrusted procedural language'
-default_version = '1.0'
-module_pathname = '$libdir/plpython2'
-relocatable = false
-schema = pg_catalog
-superuser = true
diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile
index 6af097ae68b..19d99a8045e 100644
--- a/contrib/hstore_plpython/Makefile
+++ b/contrib/hstore_plpython/Makefile
@@ -6,8 +6,8 @@ OBJS = \
 	hstore_plpython.o
 PGFILEDESC = "hstore_plpython - hstore transform for plpython"
 
-EXTENSION = hstore_plpythonu hstore_plpython2u hstore_plpython3u
-DATA = hstore_plpythonu--1.0.sql hstore_plpython2u--1.0.sql hstore_plpython3u--1.0.sql
+EXTENSION = hstore_plpython3u
+DATA = hstore_plpython3u--1.0.sql
 
 REGRESS = hstore_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
diff --git a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql b/contrib/hstore_plpython/hstore_plpython2u--1.0.sql
deleted file mode 100644
index 800765f3f0c..00000000000
--- a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/hstore_plpython/hstore_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION hstore_plpython2u" to load this file. \quit
-
-CREATE FUNCTION hstore_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'hstore_to_plpython';
-
-CREATE FUNCTION plpython2_to_hstore(val internal) RETURNS hstore
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'plpython_to_hstore';
-
-CREATE TRANSFORM FOR hstore LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION hstore_to_plpython2(internal),
-    TO SQL WITH FUNCTION plpython2_to_hstore(internal)
-);
-
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython2u IS 'transform between hstore and Python dict';
diff --git a/contrib/hstore_plpython/hstore_plpython2u.control b/contrib/hstore_plpython/hstore_plpython2u.control
deleted file mode 100644
index ed905671123..00000000000
--- a/contrib/hstore_plpython/hstore_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# hstore_plpython2u extension
-comment = 'transform between hstore and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/hstore_plpython2'
-relocatable = true
-requires = 'hstore,plpython2u'
diff --git a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql b/contrib/hstore_plpython/hstore_plpythonu--1.0.sql
deleted file mode 100644
index 52832912abc..00000000000
--- a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/hstore_plpython/hstore_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION hstore_plpythonu" to load this file. \quit
-
-CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR hstore LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION hstore_to_plpython(internal),
-    TO SQL WITH FUNCTION plpython_to_hstore(internal)
-);
-
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'transform between hstore and Python dict';
diff --git a/contrib/hstore_plpython/hstore_plpythonu.control b/contrib/hstore_plpython/hstore_plpythonu.control
deleted file mode 100644
index 8e9b35e43bf..00000000000
--- a/contrib/hstore_plpython/hstore_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# hstore_plpythonu extension
-comment = 'transform between hstore and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/hstore_plpython2'
-relocatable = true
-requires = 'hstore,plpythonu'
diff --git a/contrib/jsonb_plpython/Makefile b/contrib/jsonb_plpython/Makefile
index ca767418943..eaab5ca2601 100644
--- a/contrib/jsonb_plpython/Makefile
+++ b/contrib/jsonb_plpython/Makefile
@@ -4,12 +4,12 @@ MODULE_big = jsonb_plpython$(python_majorversion)
 OBJS = \
 	$(WIN32RES) \
 	jsonb_plpython.o
-PGFILEDESC = "jsonb_plpython - transform between jsonb and plpythonu"
+PGFILEDESC = "jsonb_plpython - jsonb transform for plpython"
 
 PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
-EXTENSION = jsonb_plpythonu jsonb_plpython2u jsonb_plpython3u
-DATA = jsonb_plpythonu--1.0.sql jsonb_plpython2u--1.0.sql jsonb_plpython3u--1.0.sql
+EXTENSION = jsonb_plpython3u
+DATA = jsonb_plpython3u--1.0.sql
 
 REGRESS = jsonb_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
diff --git a/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql b/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
deleted file mode 100644
index 2526d14ee19..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/jsonb_plpython/jsonb_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION jsonb_plpython2u" to load this file. \quit
-
-CREATE FUNCTION jsonb_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'jsonb_to_plpython';
-
-CREATE FUNCTION plpython2_to_jsonb(val internal) RETURNS jsonb
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'plpython_to_jsonb';
-
-CREATE TRANSFORM FOR jsonb LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION jsonb_to_plpython2(internal),
-    TO SQL WITH FUNCTION plpython2_to_jsonb(internal)
-);
-
-COMMENT ON TRANSFORM FOR jsonb LANGUAGE plpython2u IS 'transform between jsonb and Python';
diff --git a/contrib/jsonb_plpython/jsonb_plpython2u.control b/contrib/jsonb_plpython/jsonb_plpython2u.control
deleted file mode 100644
index d26368316b6..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# jsonb_plpython2u extension
-comment = 'transform between jsonb and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/jsonb_plpython2'
-relocatable = true
-requires = 'plpython2u'
diff --git a/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql b/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
deleted file mode 100644
index 3fa89885a63..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-/* contrib/jsonb_plpython/jsonb_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION jsonb_plpythonu" to load this file. \quit
-
-CREATE FUNCTION jsonb_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE FUNCTION plpython_to_jsonb(val internal) RETURNS jsonb
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR jsonb LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION jsonb_to_plpython(internal),
-    TO SQL WITH FUNCTION plpython_to_jsonb(internal)
-);
-
-COMMENT ON TRANSFORM FOR jsonb LANGUAGE plpythonu IS 'transform between jsonb and Python';
diff --git a/contrib/jsonb_plpython/jsonb_plpythonu.control b/contrib/jsonb_plpython/jsonb_plpythonu.control
deleted file mode 100644
index 6f8fa4f184b..00000000000
--- a/contrib/jsonb_plpython/jsonb_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# jsonb_plpythonu extension
-comment = 'transform between jsonb and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/jsonb_plpython2'
-relocatable = true
-requires = 'plpythonu'
diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile
index 12a01467721..0bccb111e6b 100644
--- a/contrib/ltree_plpython/Makefile
+++ b/contrib/ltree_plpython/Makefile
@@ -6,8 +6,8 @@ OBJS = \
 	ltree_plpython.o
 PGFILEDESC = "ltree_plpython - ltree transform for plpython"
 
-EXTENSION = ltree_plpythonu ltree_plpython2u ltree_plpython3u
-DATA = ltree_plpythonu--1.0.sql ltree_plpython2u--1.0.sql ltree_plpython3u--1.0.sql
+EXTENSION = ltree_plpython3u
+DATA = ltree_plpython3u--1.0.sql
 
 REGRESS = ltree_plpython
 REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
diff --git a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql b/contrib/ltree_plpython/ltree_plpython2u--1.0.sql
deleted file mode 100644
index 5c4a7037013..00000000000
--- a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/* contrib/ltree_plpython/ltree_plpython2u--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION ltree_plpython2u" to load this file. \quit
-
-CREATE FUNCTION ltree_to_plpython2(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME', 'ltree_to_plpython';
-
-CREATE TRANSFORM FOR ltree LANGUAGE plpython2u (
-    FROM SQL WITH FUNCTION ltree_to_plpython2(internal)
-);
diff --git a/contrib/ltree_plpython/ltree_plpython2u.control b/contrib/ltree_plpython/ltree_plpython2u.control
deleted file mode 100644
index bedfd0acbad..00000000000
--- a/contrib/ltree_plpython/ltree_plpython2u.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# ltree_plpython2u extension
-comment = 'transform between ltree and plpython2u'
-default_version = '1.0'
-module_pathname = '$libdir/ltree_plpython2'
-relocatable = true
-requires = 'ltree,plpython2u'
diff --git a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql b/contrib/ltree_plpython/ltree_plpythonu--1.0.sql
deleted file mode 100644
index ee93edf28b9..00000000000
--- a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/* contrib/ltree_plpython/ltree_plpythonu--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION ltree_plpythonu" to load this file. \quit
-
-CREATE FUNCTION ltree_to_plpython(val internal) RETURNS internal
-LANGUAGE C STRICT IMMUTABLE
-AS 'MODULE_PATHNAME';
-
-CREATE TRANSFORM FOR ltree LANGUAGE plpythonu (
-    FROM SQL WITH FUNCTION ltree_to_plpython(internal)
-);
diff --git a/contrib/ltree_plpython/ltree_plpythonu.control b/contrib/ltree_plpython/ltree_plpythonu.control
deleted file mode 100644
index b03c89a2e6e..00000000000
--- a/contrib/ltree_plpython/ltree_plpythonu.control
+++ /dev/null
@@ -1,6 +0,0 @@
-# ltree_plpythonu extension
-comment = 'transform between ltree and plpythonu'
-default_version = '1.0'
-module_pathname = '$libdir/ltree_plpython2'
-relocatable = true
-requires = 'ltree,plpythonu'
-- 
2.35.1.354.g715d08a9e5

v7-0002-plpython-Remove-regression-test-infrastructure-fo.patchtext/x-diff; charset=us-asciiDownload
From 1ebff2fb3dc84a0e139dfbd8af87032185df63c5 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 6 Mar 2022 16:57:28 -0800
Subject: [PATCH v7 2/4] plpython: Remove regression test infrastructure for
 Python 2.

Reviewed-By: Peter Eisentraut <peter@eisentraut.org>
Reviewed-By: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 src/pl/plpython/.gitignore                    |    2 -
 src/pl/plpython/Makefile                      |   14 -
 src/pl/plpython/expected/plpython_call.out    |   12 +-
 .../plpython/expected/plpython_composite.out  |   32 +-
 src/pl/plpython/expected/plpython_do.out      |    8 +-
 src/pl/plpython/expected/plpython_drop.out    |    3 +-
 src/pl/plpython/expected/plpython_ereport.out |   37 +-
 src/pl/plpython/expected/plpython_error.out   |   52 +-
 src/pl/plpython/expected/plpython_error_5.out |  447 --------
 src/pl/plpython/expected/plpython_global.out  |    6 +-
 src/pl/plpython/expected/plpython_import.out  |    8 +-
 src/pl/plpython/expected/plpython_newline.out |    6 +-
 src/pl/plpython/expected/plpython_params.out  |    8 +-
 src/pl/plpython/expected/plpython_quote.out   |    2 +-
 src/pl/plpython/expected/plpython_record.out  |   18 +-
 src/pl/plpython/expected/plpython_setof.out   |   18 +-
 src/pl/plpython/expected/plpython_spi.out     |   50 +-
 .../expected/plpython_subtransaction.out      |   38 +-
 src/pl/plpython/expected/plpython_test.out    |   12 +-
 .../expected/plpython_transaction.out         |   28 +-
 src/pl/plpython/expected/plpython_trigger.out |   46 +-
 src/pl/plpython/expected/plpython_types.out   |  230 ++--
 src/pl/plpython/expected/plpython_types_3.out | 1009 -----------------
 src/pl/plpython/expected/plpython_unicode.out |   16 +-
 src/pl/plpython/expected/plpython_void.out    |    6 +-
 src/pl/plpython/regress-python3-mangle.mk     |   38 -
 src/pl/plpython/sql/plpython_call.sql         |   12 +-
 src/pl/plpython/sql/plpython_composite.sql    |   32 +-
 src/pl/plpython/sql/plpython_do.sql           |    6 +-
 src/pl/plpython/sql/plpython_drop.sql         |    4 +-
 src/pl/plpython/sql/plpython_ereport.sql      |   26 +-
 src/pl/plpython/sql/plpython_error.sql        |   48 +-
 src/pl/plpython/sql/plpython_global.sql       |    6 +-
 src/pl/plpython/sql/plpython_import.sql       |    8 +-
 src/pl/plpython/sql/plpython_newline.sql      |    6 +-
 src/pl/plpython/sql/plpython_params.sql       |    8 +-
 src/pl/plpython/sql/plpython_quote.sql        |    2 +-
 src/pl/plpython/sql/plpython_record.sql       |   18 +-
 src/pl/plpython/sql/plpython_setof.sql        |   18 +-
 src/pl/plpython/sql/plpython_spi.sql          |   50 +-
 .../plpython/sql/plpython_subtransaction.sql  |   38 +-
 src/pl/plpython/sql/plpython_test.sql         |   12 +-
 src/pl/plpython/sql/plpython_transaction.sql  |   26 +-
 src/pl/plpython/sql/plpython_trigger.sql      |   46 +-
 src/pl/plpython/sql/plpython_types.sql        |  106 +-
 src/pl/plpython/sql/plpython_unicode.sql      |   16 +-
 src/pl/plpython/sql/plpython_void.sql         |    6 +-
 contrib/hstore_plpython/.gitignore            |    2 -
 contrib/hstore_plpython/Makefile              |    6 -
 .../expected/hstore_plpython.out              |   22 +-
 .../hstore_plpython/sql/hstore_plpython.sql   |   18 +-
 contrib/jsonb_plpython/.gitignore             |    2 -
 contrib/jsonb_plpython/Makefile               |    7 -
 .../expected/jsonb_plpython.out               |   32 +-
 contrib/jsonb_plpython/sql/jsonb_plpython.sql |   30 +-
 contrib/ltree_plpython/.gitignore             |    2 -
 contrib/ltree_plpython/Makefile               |    6 -
 .../expected/ltree_plpython.out               |   10 +-
 contrib/ltree_plpython/sql/ltree_plpython.sql |    8 +-
 src/tools/msvc/vcregress.pl                   |   76 +-
 60 files changed, 625 insertions(+), 2236 deletions(-)
 delete mode 100644 src/pl/plpython/expected/plpython_error_5.out
 delete mode 100644 src/pl/plpython/expected/plpython_types_3.out
 delete mode 100644 src/pl/plpython/regress-python3-mangle.mk

diff --git a/src/pl/plpython/.gitignore b/src/pl/plpython/.gitignore
index 70c08db3231..07bee6a29c4 100644
--- a/src/pl/plpython/.gitignore
+++ b/src/pl/plpython/.gitignore
@@ -1,7 +1,5 @@
 /spiexceptions.h
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index a83ae8865c7..0d6f74de71f 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -35,9 +35,6 @@ OBJS = \
 	plpy_util.o
 
 DATA = $(NAME)u.control $(NAME)u--1.0.sql
-ifeq ($(python_majorversion),2)
-DATA += plpythonu.control plpythonu--1.0.sql
-endif
 
 # header files to install - it's not clear which of these might be needed
 # so install them all.
@@ -77,11 +74,6 @@ endif # win32
 SHLIB_LINK = $(python_libspec) $(python_additional_libs) $(filter -lintl,$(LIBS))
 
 REGRESS_OPTS = --dbname=$(PL_TESTDB)
-# Only load plpythonu with Python 2.  The test files themselves load
-# the versioned language plpython(2|3)u.
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu
-endif
 
 REGRESS = \
 	plpython_schema \
@@ -108,8 +100,6 @@ REGRESS = \
 	plpython_transaction \
 	plpython_drop
 
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
-
 include $(top_srcdir)/src/Makefile.shlib
 
 all: all-lib
@@ -127,7 +117,6 @@ uninstall: uninstall-lib uninstall-data
 install-data: installdirs
 	$(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA)) '$(DESTDIR)$(datadir)/extension/'
 	$(INSTALL_DATA) $(addprefix $(srcdir)/, $(INCS)) '$(DESTDIR)$(includedir_server)'
-	$(INSTALL_DATA) $(srcdir)/regress-python3-mangle.mk '$(DESTDIR)$(pgxsdir)/src/pl/plpython'
 
 uninstall-data:
 	rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
@@ -136,9 +125,6 @@ uninstall-data:
 .PHONY: install-data uninstall-data
 
 
-include $(srcdir)/regress-python3-mangle.mk
-
-
 check: submake-pg-regress
 	$(pg_regress_check) $(REGRESS_OPTS) $(REGRESS)
 
diff --git a/src/pl/plpython/expected/plpython_call.out b/src/pl/plpython/expected/plpython_call.out
index 55e1027246a..4c0690067a0 100644
--- a/src/pl/plpython/expected/plpython_call.out
+++ b/src/pl/plpython/expected/plpython_call.out
@@ -2,14 +2,14 @@
 -- Tests for procedures / CALL syntax
 --
 CREATE PROCEDURE test_proc1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 pass
 $$;
 CALL test_proc1();
 -- error: can't return non-None
 CREATE PROCEDURE test_proc2()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return 5
 $$;
@@ -18,7 +18,7 @@ ERROR:  PL/Python procedure did not return None
 CONTEXT:  PL/Python procedure "test_proc2"
 CREATE TABLE test1 (a int);
 CREATE PROCEDURE test_proc3(x int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("INSERT INTO test1 VALUES (%s)" % x)
 $$;
@@ -31,7 +31,7 @@ SELECT * FROM test1;
 
 -- output arguments
 CREATE PROCEDURE test_proc5(INOUT a text)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return [a + '+' + a]
 $$;
@@ -42,7 +42,7 @@ CALL test_proc5('abc');
 (1 row)
 
 CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return (b * a, c * a)
 $$;
@@ -54,7 +54,7 @@ CALL test_proc6(2, 3, 4);
 
 -- OUT parameters
 CREATE PROCEDURE test_proc9(IN a int, OUT b int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.notice("a: %s" % (a))
 return (a * 2,)
diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out
index af801923343..bb101e07d53 100644
--- a/src/pl/plpython/expected/plpython_composite.out
+++ b/src/pl/plpython/expected/plpython_composite.out
@@ -1,6 +1,6 @@
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT multiout_simple();
  multiout_simple 
 -----------------
@@ -27,7 +27,7 @@ SELECT (multiout_simple()).j + 3;
 
 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT multiout_simple_setof();
  multiout_simple_setof 
 -----------------------
@@ -67,7 +67,7 @@ elif typ == 'obj':
     return type_record
 elif typ == 'str':
     return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
  first | second 
 -------+--------
@@ -237,7 +237,7 @@ for i in range(n):
     power = 2 ** i
     length = plpy.execute("select length('%d')" % power)[0]['length']
     yield power, length
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_setof(3);
  power_of_2 | length 
 ------------+--------
@@ -260,7 +260,7 @@ CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
 return [{'x': 4, 'y' :'four'},
         {'x': 7, 'y' :'seven'},
         {'x': 0, 'y' :'zero'}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_return_table();
  x |   y   
 ---+-------
@@ -273,7 +273,7 @@ CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS
 yield [[1], 'a']
 yield [[1,2], 'b']
 yield [[1,2,3], None]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_array();
  column1 | column2 
 ---------+---------
@@ -284,11 +284,11 @@ SELECT * FROM multiout_array();
 
 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
 return {'first': 1, 'second': 2}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
 return [{'first': 1, 'second': 2},
        {'first': 3, 'second': 4	}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM singleout_composite();
  first | second 
 -------+--------
@@ -305,7 +305,7 @@ SELECT * FROM multiout_composite();
 -- composite OUT parameters in functions returning RECORD not supported yet
 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
 return (n, (n * 2, n * 3))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
 if returnnull:
     d = None
@@ -323,7 +323,7 @@ elif typ == 'str':
     d = "(%r,%r)" % (n * 2, n * 3)
 for i in range(n):
     yield (i, d)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM multiout_composite(2);
  n | column2 
 ---+---------
@@ -438,7 +438,7 @@ CREATE TABLE changing (
 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
 return [(1, {'i': 1, 'j': 2}),
         (1, (3, 4))]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM changing_test();
  n | column2 
 ---+---------
@@ -474,7 +474,7 @@ yield {'tab': [('first', 1), ('second', 2)],
 yield {'tab': [('first', 1), ('second', 2)],
       'typ': [{'first': 'third', 'second': 3},
               {'first': 'fourth', 'second': 4}]}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_types_table();
             tab             |            typ             
 ----------------------------+----------------------------
@@ -486,7 +486,7 @@ SELECT * FROM composite_types_table();
 -- check what happens if the output record descriptor changes
 CREATE FUNCTION return_record(t text) RETURNS record AS $$
 return {'t': t, 'val': 10}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM return_record('abc') AS r(t text, val integer);
   t  | val 
 -----+-----
@@ -525,7 +525,7 @@ SELECT * FROM return_record('999') AS r(val text, t integer);
 
 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
 return {'v1':1,'v2':2,t:3}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
  v3 | v2 | v1 
 ----+----+----
@@ -572,7 +572,7 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
 -- multi-dimensional array of composite types.
 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list();
                                composite_type_as_list                               
 ------------------------------------------------------------------------------------
@@ -585,7 +585,7 @@ SELECT * FROM composite_type_as_list();
 -- on the issue.
 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
   return [['first', 1]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list_broken();
 ERROR:  malformed record literal: "first"
 DETAIL:  Missing left parenthesis.
diff --git a/src/pl/plpython/expected/plpython_do.out b/src/pl/plpython/expected/plpython_do.out
index e300530e031..d131a4c0ed6 100644
--- a/src/pl/plpython/expected/plpython_do.out
+++ b/src/pl/plpython/expected/plpython_do.out
@@ -1,8 +1,6 @@
-DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
-NOTICE:  This is plpythonu.
-DO $$ plpy.notice("This is plpython2u.") $$ LANGUAGE plpython2u;
-NOTICE:  This is plpython2u.
-DO $$ raise Exception("error test") $$ LANGUAGE plpythonu;
+DO $$ plpy.notice("This is plpython3u.") $$ LANGUAGE plpython3u;
+NOTICE:  This is plpython3u.
+DO $$ raise Exception("error test") $$ LANGUAGE plpython3u;
 ERROR:  Exception: error test
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
diff --git a/src/pl/plpython/expected/plpython_drop.out b/src/pl/plpython/expected/plpython_drop.out
index a0e3b5c4ef6..97bb54a55e7 100644
--- a/src/pl/plpython/expected/plpython_drop.out
+++ b/src/pl/plpython/expected/plpython_drop.out
@@ -2,5 +2,4 @@
 -- For paranoia's sake, don't leave an untrusted language sitting around
 --
 SET client_min_messages = WARNING;
-DROP EXTENSION plpythonu CASCADE;
-DROP EXTENSION IF EXISTS plpython2u CASCADE;
+DROP EXTENSION plpython3u CASCADE;
diff --git a/src/pl/plpython/expected/plpython_ereport.out b/src/pl/plpython/expected/plpython_ereport.out
index b73bfff5115..74dcc419ceb 100644
--- a/src/pl/plpython/expected/plpython_ereport.out
+++ b/src/pl/plpython/expected/plpython_ereport.out
@@ -17,7 +17,7 @@ plpy.info('This is message text.',
 plpy.notice('notice', detail='some detail')
 plpy.warning('warning', detail='some detail')
 plpy.error('stop on error', detail='some detail', hint='some hint')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT elog_test();
 INFO:  info
 DETAIL:  some detail
@@ -38,42 +38,42 @@ CONTEXT:  Traceback (most recent call last):
   PL/Python function "elog_test", line 18, in <module>
     plpy.error('stop on error', detail='some detail', hint='some hint')
 PL/Python function "elog_test"
-DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpythonu;
+DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  (10, 20)
 DO $$
 import time;
 from datetime import date
 plpy.info('other types', detail=date(2016, 2, 26))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  2016-02-26
 DO $$
 basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 plpy.info('other types', detail=basket)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  other types
 DETAIL:  ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 -- should fail
-DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u;
 ERROR:  ValueError: invalid SQLSTATE code
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('wrong sqlstate', sqlstate='54444A') 
 PL/Python anonymous code block
-DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: 'blabla' is an invalid keyword argument for this function
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('unsupported argument', blabla='fooboo') 
 PL/Python anonymous code block
-DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: argument 'message' given by name and position
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
     plpy.info('first message', message='second message') 
 PL/Python anonymous code block
-DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u;
 ERROR:  TypeError: argument 'message' given by name and position
 CONTEXT:  Traceback (most recent call last):
   PL/Python anonymous code block, line 1, in <module>
@@ -96,7 +96,7 @@ kwargs = {
 }
 # ignore None values
 plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT raise_exception('hello', 'world');
 ERROR:  plpy.Error: hello
 DETAIL:  world
@@ -180,26 +180,35 @@ END;
 $$;
 NOTICE:  handled exception
 DETAIL:  message:(plpy.Error: message text), detail:(detail text), hint: (hint text), sqlstate: (XX555), schema_name:(schema text), table_name:(table text), column_name:(column text), datatype_name:(datatype text), constraint_name:(constraint text)
--- The displayed context is different between Python2 and Python3,
--- but that's not important for this test.
-\set SHOW_CONTEXT never
 DO $$
 try:
     plpy.execute("select raise_exception(_message => 'my message', _sqlstate => 'XX987', _hint => 'some hint', _table_name => 'users_tab', _datatype_name => 'user_type')")
 except Exception as e:
     plpy.info(e.spidata)
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  (119577128, None, 'some hint', None, 0, None, 'users_tab', None, 'user_type', None)
 ERROR:  plpy.SPIError: plpy.Error: my message
 HINT:  some hint
+CONTEXT:  Traceback (most recent call last):
+  PL/Python anonymous code block, line 6, in <module>
+    raise e
+  PL/Python anonymous code block, line 3, in __plpython_inline_block
+    plpy.execute("select raise_exception(_message => 'my message', _sqlstate => 'XX987', _hint => 'some hint', _table_name => 'users_tab', _datatype_name => 'user_type')")
+PL/Python anonymous code block
 DO $$
 try:
     plpy.error(message  = 'my message', sqlstate = 'XX987', hint = 'some hint', table_name = 'users_tab', datatype_name = 'user_type')
 except Exception as e:
     plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name))
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 INFO:  sqlstate: XX987, hint: some hint, table_name: users_tab, datatype_name: user_type
 ERROR:  plpy.Error: my message
 HINT:  some hint
+CONTEXT:  Traceback (most recent call last):
+  PL/Python anonymous code block, line 6, in <module>
+    raise e
+  PL/Python anonymous code block, line 3, in __plpython_inline_block
+    plpy.error(message  = 'my message', sqlstate = 'XX987', hint = 'some hint', table_name = 'users_tab', datatype_name = 'user_type')
+PL/Python anonymous code block
diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index b2f8fe83eb6..7fe864a1a57 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -6,7 +6,7 @@
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 ERROR:  could not compile PL/Python function "python_syntax_error"
 DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
 /* With check_function_bodies = false the function should get defined
@@ -16,7 +16,7 @@ SET check_function_bodies = false;
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 SELECT python_syntax_error();
 ERROR:  could not compile PL/Python function "python_syntax_error"
 DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
@@ -30,7 +30,7 @@ RESET check_function_bodies;
 CREATE FUNCTION sql_syntax_error() RETURNS text
         AS
 'plpy.execute("syntax error")'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 SELECT sql_syntax_error();
 ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
 LINE 1: syntax error
@@ -45,7 +45,7 @@ PL/Python function "sql_syntax_error"
 CREATE FUNCTION exception_index_invalid(text) RETURNS text
 	AS
 'return args[1]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT exception_index_invalid('test');
 ERROR:  IndexError: list index out of range
 CONTEXT:  Traceback (most recent call last):
@@ -58,7 +58,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text
 	AS
 'rv = plpy.execute("SELECT test5(''foo'')")
 return rv[0]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT exception_index_invalid_nested();
 ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
 LINE 1: SELECT test5('foo')
@@ -81,7 +81,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_uncaught('rick');
 ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
 CONTEXT:  Traceback (most recent call last):
@@ -105,7 +105,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_caught('rick');
 NOTICE:  type "test" does not exist
  invalid_type_caught 
@@ -129,7 +129,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT invalid_type_reraised('rick');
 ERROR:  plpy.Error: type "test" does not exist
 CONTEXT:  Traceback (most recent call last):
@@ -147,7 +147,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT valid_type('rick');
  valid_type 
 ------------
@@ -170,7 +170,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_error();
 ERROR:  plpy.Error: boom
 CONTEXT:  Traceback (most recent call last):
@@ -199,7 +199,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_error_raise();
 ERROR:  plpy.Error: boom
 CONTEXT:  Traceback (most recent call last):
@@ -228,7 +228,7 @@ def fun3():
 fun3()
 return "you''ve been warned"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 SELECT nested_warning();
 WARNING:  boom
    nested_warning   
@@ -241,9 +241,9 @@ WARNING:  boom
 CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
 $$
 plpy.nonexistent
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT toplevel_attribute_error();
-ERROR:  AttributeError: 'module' object has no attribute 'nonexistent'
+ERROR:  AttributeError: module 'plpy' has no attribute 'nonexistent'
 CONTEXT:  Traceback (most recent call last):
   PL/Python function "toplevel_attribute_error", line 2, in <module>
     plpy.nonexistent
@@ -261,7 +261,7 @@ def third():
   plpy.execute("select sql_error()")
 
 first()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
 begin
   select 1/0;
@@ -274,7 +274,7 @@ end
 $$ LANGUAGE plpgsql;
 CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
 plpy.execute("select sql_error()")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT python_traceback();
 ERROR:  spiexceptions.DivisionByZero: division by zero
 CONTEXT:  Traceback (most recent call last):
@@ -325,7 +325,7 @@ except spiexceptions.NotNullViolation as e:
     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
 except spiexceptions.UniqueViolation as e:
     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT specific_exception(2);
  specific_exception 
 --------------------
@@ -351,7 +351,7 @@ NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
 CREATE FUNCTION python_unique_violation() RETURNS void AS $$
 plpy.execute("insert into specific values (1)")
 plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
 begin
     begin
@@ -374,7 +374,7 @@ CREATE FUNCTION manual_subxact() RETURNS void AS $$
 plpy.execute("savepoint save")
 plpy.execute("create table foo(x integer)")
 plpy.execute("rollback to save")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT manual_subxact();
 ERROR:  plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
 CONTEXT:  Traceback (most recent call last):
@@ -389,7 +389,7 @@ rollback = plpy.prepare("rollback to save")
 plpy.execute(save)
 plpy.execute("create table foo(x integer)")
 plpy.execute(rollback)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT manual_subxact_prepared();
 ERROR:  plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
 CONTEXT:  Traceback (most recent call last):
@@ -400,7 +400,7 @@ PL/Python function "manual_subxact_prepared"
  */
 CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
 raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 DO $$
 BEGIN
 	SELECT plpy_raise_spiexception();
@@ -414,7 +414,7 @@ CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
 exc = plpy.spiexceptions.DivisionByZero()
 exc.sqlstate = 'SILLY'
 raise exc
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 DO $$
 BEGIN
 	SELECT plpy_raise_spiexception_override();
@@ -425,18 +425,18 @@ $$ LANGUAGE plpgsql;
 /* test the context stack trace for nested execution levels
  */
 CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
 plpy.execute("SELECT notice_innerfunc()")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 \set SHOW_CONTEXT always
 SELECT notice_outerfunc();
 NOTICE:  inside DO
 CONTEXT:  PL/Python anonymous code block
-SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
+SQL statement "DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$"
 PL/Python function "notice_innerfunc"
 SQL statement "SELECT notice_innerfunc()"
 PL/Python function "notice_outerfunc"
diff --git a/src/pl/plpython/expected/plpython_error_5.out b/src/pl/plpython/expected/plpython_error_5.out
deleted file mode 100644
index bc66ab55340..00000000000
--- a/src/pl/plpython/expected/plpython_error_5.out
+++ /dev/null
@@ -1,447 +0,0 @@
--- test error handling, i forgot to restore Warn_restart in
--- the trigger handler once. the errors and subsequent core dump were
--- interesting.
-/* Flat out Python syntax error
- */
-CREATE FUNCTION python_syntax_error() RETURNS text
-        AS
-'.syntaxerror'
-        LANGUAGE plpython3u;
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-/* With check_function_bodies = false the function should get defined
- * and the error reported when called
- */
-SET check_function_bodies = false;
-CREATE FUNCTION python_syntax_error() RETURNS text
-        AS
-'.syntaxerror'
-        LANGUAGE plpython3u;
-SELECT python_syntax_error();
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-/* Run the function twice to check if the hashtable entry gets cleaned up */
-SELECT python_syntax_error();
-ERROR:  could not compile PL/Python function "python_syntax_error"
-DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
-RESET check_function_bodies;
-/* Flat out syntax error
- */
-CREATE FUNCTION sql_syntax_error() RETURNS text
-        AS
-'plpy.execute("syntax error")'
-        LANGUAGE plpython3u;
-SELECT sql_syntax_error();
-ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
-LINE 1: syntax error
-        ^
-QUERY:  syntax error
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "sql_syntax_error", line 1, in <module>
-    plpy.execute("syntax error")
-PL/Python function "sql_syntax_error"
-/* check the handling of uncaught python exceptions
- */
-CREATE FUNCTION exception_index_invalid(text) RETURNS text
-	AS
-'return args[1]'
-	LANGUAGE plpython3u;
-SELECT exception_index_invalid('test');
-ERROR:  IndexError: list index out of range
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "exception_index_invalid", line 1, in <module>
-    return args[1]
-PL/Python function "exception_index_invalid"
-/* check handling of nested exceptions
- */
-CREATE FUNCTION exception_index_invalid_nested() RETURNS text
-	AS
-'rv = plpy.execute("SELECT test5(''foo'')")
-return rv[0]'
-	LANGUAGE plpython3u;
-SELECT exception_index_invalid_nested();
-ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
-LINE 1: SELECT test5('foo')
-               ^
-HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
-QUERY:  SELECT test5('foo')
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "exception_index_invalid_nested", line 1, in <module>
-    rv = plpy.execute("SELECT test5('foo')")
-PL/Python function "exception_index_invalid_nested"
-/* a typo
- */
-CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	SD["plan"] = plpy.prepare(q, [ "test" ])
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_uncaught('rick');
-ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "invalid_type_uncaught", line 3, in <module>
-    SD["plan"] = plpy.prepare(q, [ "test" ])
-PL/Python function "invalid_type_uncaught"
-/* for what it's worth catch the exception generated by
- * the typo, and return None
- */
-CREATE FUNCTION invalid_type_caught(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	try:
-		SD["plan"] = plpy.prepare(q, [ "test" ])
-	except plpy.SPIError as ex:
-		plpy.notice(str(ex))
-		return None
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_caught('rick');
-NOTICE:  type "test" does not exist
- invalid_type_caught 
----------------------
- 
-(1 row)
-
-/* for what it's worth catch the exception generated by
- * the typo, and reraise it as a plain error
- */
-CREATE FUNCTION invalid_type_reraised(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	q = "SELECT fname FROM users WHERE lname = $1"
-	try:
-		SD["plan"] = plpy.prepare(q, [ "test" ])
-	except plpy.SPIError as ex:
-		plpy.error(str(ex))
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT invalid_type_reraised('rick');
-ERROR:  plpy.Error: type "test" does not exist
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "invalid_type_reraised", line 6, in <module>
-    plpy.error(str(ex))
-PL/Python function "invalid_type_reraised"
-/* no typo no messing about
- */
-CREATE FUNCTION valid_type(a text) RETURNS text
-	AS
-'if "plan" not in SD:
-	SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ])
-rv = plpy.execute(SD["plan"], [ a ])
-if len(rv):
-	return rv[0]["fname"]
-return None
-'
-	LANGUAGE plpython3u;
-SELECT valid_type('rick');
- valid_type 
-------------
- 
-(1 row)
-
-/* error in nested functions to get a traceback
-*/
-CREATE FUNCTION nested_error() RETURNS text
-	AS
-'def fun1():
-	plpy.error("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "not reached"
-'
-	LANGUAGE plpython3u;
-SELECT nested_error();
-ERROR:  plpy.Error: boom
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "nested_error", line 10, in <module>
-    fun3()
-  PL/Python function "nested_error", line 8, in fun3
-    fun2()
-  PL/Python function "nested_error", line 5, in fun2
-    fun1()
-  PL/Python function "nested_error", line 2, in fun1
-    plpy.error("boom")
-PL/Python function "nested_error"
-/* raising plpy.Error is just like calling plpy.error
-*/
-CREATE FUNCTION nested_error_raise() RETURNS text
-	AS
-'def fun1():
-	raise plpy.Error("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "not reached"
-'
-	LANGUAGE plpython3u;
-SELECT nested_error_raise();
-ERROR:  plpy.Error: boom
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "nested_error_raise", line 10, in <module>
-    fun3()
-  PL/Python function "nested_error_raise", line 8, in fun3
-    fun2()
-  PL/Python function "nested_error_raise", line 5, in fun2
-    fun1()
-  PL/Python function "nested_error_raise", line 2, in fun1
-    raise plpy.Error("boom")
-PL/Python function "nested_error_raise"
-/* using plpy.warning should not produce a traceback
-*/
-CREATE FUNCTION nested_warning() RETURNS text
-	AS
-'def fun1():
-	plpy.warning("boom")
-
-def fun2():
-	fun1()
-
-def fun3():
-	fun2()
-
-fun3()
-return "you''ve been warned"
-'
-	LANGUAGE plpython3u;
-SELECT nested_warning();
-WARNING:  boom
-   nested_warning   
---------------------
- you've been warned
-(1 row)
-
-/* AttributeError at toplevel used to give segfaults with the traceback
-*/
-CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
-$$
-plpy.nonexistent
-$$ LANGUAGE plpython3u;
-SELECT toplevel_attribute_error();
-ERROR:  AttributeError: module 'plpy' has no attribute 'nonexistent'
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "toplevel_attribute_error", line 2, in <module>
-    plpy.nonexistent
-PL/Python function "toplevel_attribute_error"
-/* Calling PL/Python functions from SQL and vice versa should not lose context.
- */
-CREATE OR REPLACE FUNCTION python_traceback() RETURNS void AS $$
-def first():
-  second()
-
-def second():
-  third()
-
-def third():
-  plpy.execute("select sql_error()")
-
-first()
-$$ LANGUAGE plpython3u;
-CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
-begin
-  select 1/0;
-end
-$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION python_from_sql_error() RETURNS void AS $$
-begin
-  select python_traceback();
-end
-$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
-plpy.execute("select sql_error()")
-$$ LANGUAGE plpython3u;
-SELECT python_traceback();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "python_traceback", line 11, in <module>
-    first()
-  PL/Python function "python_traceback", line 3, in first
-    second()
-  PL/Python function "python_traceback", line 6, in second
-    third()
-  PL/Python function "python_traceback", line 9, in third
-    plpy.execute("select sql_error()")
-PL/Python function "python_traceback"
-SELECT sql_error();
-ERROR:  division by zero
-CONTEXT:  SQL statement "select 1/0"
-PL/pgSQL function sql_error() line 3 at SQL statement
-SELECT python_from_sql_error();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "python_traceback", line 11, in <module>
-    first()
-  PL/Python function "python_traceback", line 3, in first
-    second()
-  PL/Python function "python_traceback", line 6, in second
-    third()
-  PL/Python function "python_traceback", line 9, in third
-    plpy.execute("select sql_error()")
-PL/Python function "python_traceback"
-SQL statement "select python_traceback()"
-PL/pgSQL function python_from_sql_error() line 3 at SQL statement
-SELECT sql_from_python_error();
-ERROR:  spiexceptions.DivisionByZero: division by zero
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "sql_from_python_error", line 2, in <module>
-    plpy.execute("select sql_error()")
-PL/Python function "sql_from_python_error"
-/* check catching specific types of exceptions
- */
-CREATE TABLE specific (
-    i integer PRIMARY KEY
-);
-CREATE FUNCTION specific_exception(i integer) RETURNS void AS
-$$
-from plpy import spiexceptions
-try:
-    plpy.execute("insert into specific values (%s)" % (i or "NULL"));
-except spiexceptions.NotNullViolation as e:
-    plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
-except spiexceptions.UniqueViolation as e:
-    plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpython3u;
-SELECT specific_exception(2);
- specific_exception 
---------------------
- 
-(1 row)
-
-SELECT specific_exception(NULL);
-NOTICE:  Violated the NOT NULL constraint, sqlstate 23502
- specific_exception 
---------------------
- 
-(1 row)
-
-SELECT specific_exception(2);
-NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
- specific_exception 
---------------------
- 
-(1 row)
-
-/* SPI errors in PL/Python functions should preserve the SQLSTATE value
- */
-CREATE FUNCTION python_unique_violation() RETURNS void AS $$
-plpy.execute("insert into specific values (1)")
-plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpython3u;
-CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
-begin
-    begin
-        perform python_unique_violation();
-    exception when unique_violation then
-        return 'ok';
-    end;
-    return 'not reached';
-end;
-$$ language plpgsql;
-SELECT catch_python_unique_violation();
- catch_python_unique_violation 
--------------------------------
- ok
-(1 row)
-
-/* manually starting subtransactions - a bad idea
- */
-CREATE FUNCTION manual_subxact() RETURNS void AS $$
-plpy.execute("savepoint save")
-plpy.execute("create table foo(x integer)")
-plpy.execute("rollback to save")
-$$ LANGUAGE plpython3u;
-SELECT manual_subxact();
-ERROR:  plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "manual_subxact", line 2, in <module>
-    plpy.execute("savepoint save")
-PL/Python function "manual_subxact"
-/* same for prepared plans
- */
-CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
-save = plpy.prepare("savepoint save")
-rollback = plpy.prepare("rollback to save")
-plpy.execute(save)
-plpy.execute("create table foo(x integer)")
-plpy.execute(rollback)
-$$ LANGUAGE plpython3u;
-SELECT manual_subxact_prepared();
-ERROR:  plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "manual_subxact_prepared", line 4, in <module>
-    plpy.execute(save)
-PL/Python function "manual_subxact_prepared"
-/* raising plpy.spiexception.* from python code should preserve sqlstate
- */
-CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
-raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpython3u;
-DO $$
-BEGIN
-	SELECT plpy_raise_spiexception();
-EXCEPTION WHEN division_by_zero THEN
-	-- NOOP
-END
-$$ LANGUAGE plpgsql;
-/* setting a custom sqlstate should be handled
- */
-CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
-exc = plpy.spiexceptions.DivisionByZero()
-exc.sqlstate = 'SILLY'
-raise exc
-$$ LANGUAGE plpython3u;
-DO $$
-BEGIN
-	SELECT plpy_raise_spiexception_override();
-EXCEPTION WHEN SQLSTATE 'SILLY' THEN
-	-- NOOP
-END
-$$ LANGUAGE plpgsql;
-/* test the context stack trace for nested execution levels
- */
-CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
-return 1
-$$ LANGUAGE plpythonu;
-CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
-plpy.execute("SELECT notice_innerfunc()")
-return 1
-$$ LANGUAGE plpythonu;
-\set SHOW_CONTEXT always
-SELECT notice_outerfunc();
-NOTICE:  inside DO
-CONTEXT:  PL/Python anonymous code block
-SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
-PL/Python function "notice_innerfunc"
-SQL statement "SELECT notice_innerfunc()"
-PL/Python function "notice_outerfunc"
- notice_outerfunc 
-------------------
-                1
-(1 row)
-
diff --git a/src/pl/plpython/expected/plpython_global.out b/src/pl/plpython/expected/plpython_global.out
index 192e3e48a72..a4cfb1483f9 100644
--- a/src/pl/plpython/expected/plpython_global.out
+++ b/src/pl/plpython/expected/plpython_global.out
@@ -8,7 +8,7 @@ CREATE FUNCTION global_test_one() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_one"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION global_test_two() returns text
     AS
 'if "global_test" not in SD:
@@ -16,7 +16,7 @@ CREATE FUNCTION global_test_two() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_two"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION static_test() returns int4
     AS
 'if "call" in SD:
@@ -25,7 +25,7 @@ else:
 	SD["call"] = 1
 return SD["call"]
 '
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 SELECT static_test();
  static_test 
 -------------
diff --git a/src/pl/plpython/expected/plpython_import.out b/src/pl/plpython/expected/plpython_import.out
index b59e1821a79..854e989eaf9 100644
--- a/src/pl/plpython/expected/plpython_import.out
+++ b/src/pl/plpython/expected/plpython_import.out
@@ -6,7 +6,7 @@ CREATE FUNCTION import_fail() returns text
 except ImportError:
 	return "failed as expected"
 return "succeeded, that wasn''t supposed to happen"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION import_succeed() returns text
 	AS
 'try:
@@ -25,7 +25,7 @@ except Exception as ex:
 	plpy.notice("import failed -- %s" % str(ex))
 	return "failed, that wasn''t supposed to happen"
 return "succeeded, as expected"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 CREATE FUNCTION import_test_one(p text) RETURNS text
 	AS
 'try:
@@ -35,7 +35,7 @@ except ImportError:
     import sha
     digest = sha.new(p)
 return digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION import_test_two(u users) RETURNS text
 	AS
 'plain = u["fname"] + u["lname"]
@@ -46,7 +46,7 @@ except ImportError:
     import sha
     digest = sha.new(plain);
 return "sha hash of " + plain + " is " + digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 -- import python modules
 --
 SELECT import_fail();
diff --git a/src/pl/plpython/expected/plpython_newline.out b/src/pl/plpython/expected/plpython_newline.out
index 27dc2f8ab0c..2bc149257e7 100644
--- a/src/pl/plpython/expected/plpython_newline.out
+++ b/src/pl/plpython/expected/plpython_newline.out
@@ -3,13 +3,13 @@
 --
 CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS
 E'x = 100\ny = 23\nreturn x + y\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS
 E'x = 100\ry = 23\rreturn x + y\r'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS
 E'x = 100\r\ny = 23\r\nreturn x + y\r\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 SELECT newline_lf();
  newline_lf 
 ------------
diff --git a/src/pl/plpython/expected/plpython_params.out b/src/pl/plpython/expected/plpython_params.out
index 46ea7dfb90b..d1a36f36239 100644
--- a/src/pl/plpython/expected/plpython_params.out
+++ b/src/pl/plpython/expected/plpython_params.out
@@ -3,12 +3,12 @@
 --
 CREATE FUNCTION test_param_names0(integer, integer) RETURNS int AS $$
 return args[0] + args[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_param_names1(a0 integer, a1 text) RETURNS boolean AS $$
 assert a0 == args[0]
 assert a1 == args[1]
 return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_param_names2(u users) RETURNS text AS $$
 assert u == args[0]
 if isinstance(u, dict):
@@ -19,7 +19,7 @@ if isinstance(u, dict):
 else:
     s = str(u)
 return s
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- use deliberately wrong parameter names
 CREATE FUNCTION test_param_names3(a0 integer) RETURNS boolean AS $$
 try:
@@ -28,7 +28,7 @@ try:
 except NameError as e:
 	assert e.args[0].find("a1") > -1
 	return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_param_names0(2,7);
  test_param_names0 
 -------------------
diff --git a/src/pl/plpython/expected/plpython_quote.out b/src/pl/plpython/expected/plpython_quote.out
index eed72923aec..1fbe93d5351 100644
--- a/src/pl/plpython/expected/plpython_quote.out
+++ b/src/pl/plpython/expected/plpython_quote.out
@@ -8,7 +8,7 @@ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
         return plpy.quote_ident(t)
     else:
         raise plpy.Error("unrecognized quote type %s" % how)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT quote(t, 'literal') FROM (VALUES
        ('abc'),
        ('a''bc'),
diff --git a/src/pl/plpython/expected/plpython_record.out b/src/pl/plpython/expected/plpython_record.out
index 458330713a8..31de198582b 100644
--- a/src/pl/plpython/expected/plpython_record.out
+++ b/src/pl/plpython/expected/plpython_record.out
@@ -23,7 +23,7 @@ elif typ == 'obj':
 	type_record.first = first
 	type_record.second = second
 	return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
 if retnull:
 	return None
@@ -40,17 +40,17 @@ elif typ == 'obj':
 	return type_record
 elif typ == 'str':
 	return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
 return first + '_in_to_out';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- Test tuple returning functions
 SELECT * FROM test_table_record_as('dict', null, null, false);
  first | second 
@@ -340,7 +340,7 @@ SELECT * FROM test_type_record_as('obj', 'one', 1, false);
 -- errors cases
 CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
     return { 'first': 'first' }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error1();
 ERROR:  key "second" not found in mapping
 HINT:  To return null in a column, add the value None to the mapping with the key named after the column.
@@ -348,7 +348,7 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_record_error1"
 CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
     return [ 'first' ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error2();
 ERROR:  length of returned sequence did not match number of columns in row
 CONTEXT:  while creating return value
@@ -357,7 +357,7 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
     class type_record: pass
     type_record.first = 'first'
     return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error3();
 ERROR:  attribute "second" does not exist in Python object
 HINT:  To return null in a column, let the returned object have an attribute named after column with value None.
@@ -365,7 +365,7 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_record_error3"
 CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
     return 'foo'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_record_error4();
 ERROR:  malformed record literal: "foo"
 DETAIL:  Missing left parenthesis.
diff --git a/src/pl/plpython/expected/plpython_setof.out b/src/pl/plpython/expected/plpython_setof.out
index 170dbc394de..39409400290 100644
--- a/src/pl/plpython/expected/plpython_setof.out
+++ b/src/pl/plpython/expected/plpython_setof.out
@@ -3,20 +3,20 @@
 --
 CREATE FUNCTION test_setof_error() RETURNS SETOF text AS $$
 return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_setof_error();
 ERROR:  returned object cannot be iterated
 DETAIL:  PL/Python set-returning functions must return an iterable object.
 CONTEXT:  PL/Python function "test_setof_error"
 CREATE FUNCTION test_setof_as_list(count integer, content text) RETURNS SETOF text AS $$
 return [ content ]*count
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_as_tuple(count integer, content text) RETURNS SETOF text AS $$
 t = ()
 for i in range(count):
 	t += ( content, )
 return t
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_as_iterator(count integer, content text) RETURNS SETOF text AS $$
 class producer:
 	def __init__ (self, icount, icontent):
@@ -24,13 +24,13 @@ class producer:
 		self.icount = icount
 	def __iter__ (self):
 		return self
-	def next (self):
+	def __next__ (self):
 		if self.icount == 0:
 			raise StopIteration
 		self.icount -= 1
 		return self.icontent
 return producer(count, content)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_setof_spi_in_iterator() RETURNS SETOF text AS
 $$
     for s in ('Hello', 'Brave', 'New', 'World'):
@@ -38,7 +38,7 @@ $$
         yield s
         plpy.execute('select 2')
 $$
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 -- Test set returning functions
 SELECT test_setof_as_list(0, 'list');
  test_setof_as_list 
@@ -130,7 +130,7 @@ global x
 while x <= lim:
     yield x
     x = x + 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT ugly(1, 5);
  ugly 
 ------
@@ -155,7 +155,7 @@ CREATE OR REPLACE FUNCTION get_user_records()
 RETURNS SETOF users
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT get_user_records();
    get_user_records   
 ----------------------
@@ -179,7 +179,7 @@ CREATE OR REPLACE FUNCTION get_user_records2()
 RETURNS TABLE(fname text, lname text, username text, userid int)
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT get_user_records2();
   get_user_records2   
 ----------------------
diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out
index a09df68c7d1..8853e2540d3 100644
--- a/src/pl/plpython/expected/plpython_spi.out
+++ b/src/pl/plpython/expected/plpython_spi.out
@@ -6,17 +6,17 @@ CREATE FUNCTION nested_call_one(a text) RETURNS text
 'q = "SELECT nested_call_two(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 CREATE FUNCTION nested_call_two(a text) RETURNS text
 	AS
 'q = "SELECT nested_call_three(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 CREATE FUNCTION nested_call_three(a text) RETURNS text
 	AS
 'return a'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 -- some spi stuff
 CREATE FUNCTION spi_prepared_plan_test_one(a text) RETURNS text
 	AS
@@ -30,7 +30,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
 	AS
 'if "myplan" not in SD:
@@ -43,7 +43,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
 	AS
 'if "myplan" not in SD:
@@ -57,7 +57,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION join_sequences(s sequences) RETURNS text
 	AS
 'if not s["multipart"]:
@@ -69,7 +69,7 @@ for r in rv:
 	seq = seq + r["sequence"]
 return seq
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
 	AS
 'r = 0
@@ -77,7 +77,7 @@ if a > 1:
     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
 return a + r
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 --
 -- spi and nested calls
 --
@@ -155,7 +155,7 @@ if result.status() > 0:
    return result.nrows()
 else:
    return None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
 INFO:  True
 INFO:  ['foo', 'bar']
@@ -177,7 +177,7 @@ CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return result.nrows()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_nrows_test($$SELECT 1$$);
  result_nrows_test 
 -------------------
@@ -206,7 +206,7 @@ CREATE FUNCTION result_len_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return len(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_len_test($$SELECT 1$$);
  result_len_test 
 -----------------
@@ -246,7 +246,7 @@ result[-1] = {'c': 1000}
 result[:2] = [{'c': 10}, {'c': 100}]
 plpy.info([item['c'] for item in result[:]])
 
-# raises TypeError, but the message differs on Python 2.6, so silence it
+# raises TypeError, catch so further tests could be added
 try:
     plpy.info(result['foo'])
 except TypeError:
@@ -254,7 +254,7 @@ except TypeError:
 else:
     assert False, "TypeError not raised"
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_subscript_test();
 INFO:  2
 INFO:  4
@@ -272,7 +272,7 @@ result = plpy.execute("select 1 where false")
 
 plpy.info(result[:])
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_empty_test();
 INFO:  []
  result_empty_test 
@@ -285,7 +285,7 @@ AS $$
 plan = plpy.prepare(cmd)
 result = plpy.execute(plan)
 return str(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
                       result_str_test                       
 ------------------------------------------------------------
@@ -306,12 +306,12 @@ for row in res:
     if row['lname'] == 'doe':
         does += 1
 return does
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
 res.close()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 assert len(res.fetch(3)) == 3
@@ -329,7 +329,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users order by fname")
 assert len(res.fetch(2)) == 2
@@ -342,7 +342,7 @@ except AttributeError:
 assert item['fname'] == 'rick'
 
 assert len(res.fetch(2)) == 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
@@ -352,7 +352,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION next_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
@@ -365,7 +365,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users where false")
 assert len(res.fetch(1)) == 0
@@ -378,7 +378,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
 plan = plpy.prepare(
     "select fname, lname from users where fname like $1 || '%' order by fname",
@@ -387,12 +387,12 @@ for row in plpy.cursor(plan, ["w"]):
     yield row['fname']
 for row in plan.cursor(["j"]):
     yield row['fname']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
                     ["text"])
 c = plpy.cursor(plan, ["a", "b"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TYPE test_composite_type AS (
   a1 int,
   a2 varchar
@@ -401,7 +401,7 @@ CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS
 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
 return res[0]["c1"]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT simple_cursor_test();
  simple_cursor_test 
 --------------------
diff --git a/src/pl/plpython/expected/plpython_subtransaction.out b/src/pl/plpython/expected/plpython_subtransaction.out
index 2a56541917d..43d9277a33b 100644
--- a/src/pl/plpython/expected/plpython_subtransaction.out
+++ b/src/pl/plpython/expected/plpython_subtransaction.out
@@ -14,7 +14,7 @@ with plpy.subtransaction():
         plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
     elif what_error == "Python":
         raise Exception("Python exception")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_ctx_test();
  subtransaction_ctx_test 
 -------------------------
@@ -71,7 +71,7 @@ with plpy.subtransaction():
             raise
         plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_nested_test();
 ERROR:  spiexceptions.SyntaxError: syntax error at or near "error"
 LINE 1: error
@@ -111,7 +111,7 @@ with plpy.subtransaction():
     plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
     plpy.execute("SELECT subtransaction_nested_test('t')")
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_deeply_nested_test();
 NOTICE:  Swallowed SyntaxError('syntax error at or near "error"')
  subtransaction_deeply_nested_test 
@@ -133,42 +133,42 @@ TRUNCATE subtransaction_tbl;
 CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
 AS $$
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__exit__(None, None, None)
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
 AS $$
 s = plpy.subtransaction()
 s.__enter__()
 s.__exit__(None, None, None)
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
 AS $$
 s = plpy.subtransaction()
 s.__enter__()
 s.__enter__()
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- No warnings here, as the subtransaction gets indeed closed
 CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
 AS $$
 with plpy.subtransaction() as s:
     s.__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
 AS $$
 try:
@@ -176,7 +176,7 @@ try:
         s.__exit__(None, None, None)
 except ValueError as e:
     raise ValueError(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_exit_without_enter();
 ERROR:  ValueError: this subtransaction has not been entered
 CONTEXT:  Traceback (most recent call last):
@@ -255,7 +255,7 @@ try:
     plpy.execute(p, ["wrong"])
 except plpy.SPIError:
     plpy.warning("Caught a SPI error")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_mix_explicit_and_implicit();
 WARNING:  Caught a SPI error from an explicit subtransaction
 WARNING:  Caught a SPI error
@@ -278,7 +278,7 @@ AS $$
 s = plpy.subtransaction()
 s.enter()
 s.exit(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT subtransaction_alternative_names();
  subtransaction_alternative_names 
 ----------------------------------
@@ -294,7 +294,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT try_catch_inside_subtransaction();
 NOTICE:  caught
  try_catch_inside_subtransaction 
@@ -318,7 +318,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT pk_violation_inside_subtransaction();
 NOTICE:  caught
  pk_violation_inside_subtransaction 
@@ -340,7 +340,7 @@ with plpy.subtransaction():
     cur.fetch(10)
 fetched = cur.fetch(10);
 return int(fetched[5]["i"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
 try:
     with plpy.subtransaction():
@@ -351,7 +351,7 @@ except plpy.SPIError:
     fetched = cur.fetch(10)
     return int(fetched[5]["i"])
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
 try:
     with plpy.subtransaction():
@@ -364,7 +364,7 @@ except plpy.SPIError:
     fetched = cur.fetch(5)
     return fetched[2]["i"]
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
 try:
     with plpy.subtransaction():
@@ -374,7 +374,7 @@ except plpy.SPIError:
     cur.close()
     return True
 return False # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT cursor_in_subxact();
  cursor_in_subxact 
 -------------------
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index 39b994f4468..13c14119c08 100644
--- a/src/pl/plpython/expected/plpython_test.out
+++ b/src/pl/plpython/expected/plpython_test.out
@@ -1,7 +1,7 @@
 -- first some tests of basic functionality
-CREATE EXTENSION plpython2u;
+CREATE EXTENSION plpython3u;
 -- really stupid function just to get the module loaded
-CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
+CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 select stupid();
  stupid 
 --------
@@ -9,7 +9,7 @@ select stupid();
 (1 row)
 
 -- check 2/3 versioning
-CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
+CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 select stupidn();
  stupidn 
 ---------
@@ -26,7 +26,7 @@ for key in keys:
     out.append("%s: %s" % (key, u[key]))
 words = a1 + " " + a2 + " => {" + ", ".join(out) + "}"
 return words'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 select "Argument test #1"(users, fname, lname) from users where lname = 'doe' order by 1;
                            Argument test #1                            
 -----------------------------------------------------------------------
@@ -41,7 +41,7 @@ $$
 contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
 contents.sort()
 return contents
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select module_contents();
  module_contents 
 -----------------
@@ -78,7 +78,7 @@ plpy.info('info', 37, [1, 2, 3])
 plpy.notice('notice')
 plpy.warning('warning')
 plpy.error('error')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT elog_test_basic();
 INFO:  info
 INFO:  37
diff --git a/src/pl/plpython/expected/plpython_transaction.out b/src/pl/plpython/expected/plpython_transaction.out
index 72d1e45a768..659ccefc797 100644
--- a/src/pl/plpython/expected/plpython_transaction.out
+++ b/src/pl/plpython/expected/plpython_transaction.out
@@ -1,6 +1,6 @@
 CREATE TABLE test1 (a int, b text);
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -22,7 +22,7 @@ SELECT * FROM test1;
 
 TRUNCATE test1;
 DO
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -44,7 +44,7 @@ SELECT * FROM test1;
 TRUNCATE test1;
 -- not allowed in a function
 CREATE FUNCTION transaction_test2() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -67,7 +67,7 @@ SELECT * FROM test1;
 
 -- also not allowed if procedure is called from a function
 CREATE FUNCTION transaction_test3() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("CALL transaction_test1()")
 return 1
@@ -85,19 +85,19 @@ SELECT * FROM test1;
 
 -- DO block inside function
 CREATE FUNCTION transaction_test4() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 return 1
 $$;
 SELECT transaction_test4();
 ERROR:  spiexceptions.InvalidTransactionTermination: spiexceptions.InvalidTransactionTermination: invalid transaction termination
 CONTEXT:  Traceback (most recent call last):
   PL/Python function "transaction_test4", line 2, in <module>
-    plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+    plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 PL/Python function "transaction_test4"
 -- commit inside subtransaction (prohibited)
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 s = plpy.subtransaction()
 s.enter()
 plpy.commit()
@@ -112,7 +112,7 @@ PL/Python anonymous code block
 CREATE TABLE test2 (x int);
 INSERT INTO test2 VALUES (0), (1), (2), (3), (4);
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.commit()
@@ -135,7 +135,7 @@ SELECT * FROM pg_cursors;
 
 -- error in cursor loop with commit
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x'])
     plpy.commit()
@@ -159,7 +159,7 @@ SELECT * FROM pg_cursors;
 
 -- rollback inside cursor loop
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.rollback()
@@ -176,7 +176,7 @@ SELECT * FROM pg_cursors;
 
 -- first commit then rollback inside cursor loop
 TRUNCATE test1;
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     if row['x'] % 2 == 0:
@@ -200,7 +200,7 @@ SELECT * FROM pg_cursors;
 -- check handling of an error during COMMIT
 CREATE TABLE testpk (id int PRIMARY KEY);
 CREATE TABLE testfk(f1 int REFERENCES testpk DEFERRABLE INITIALLY DEFERRED);
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 # this insert will fail during commit:
 plpy.execute("INSERT INTO testfk VALUES (0)")
 plpy.commit()
@@ -222,7 +222,7 @@ SELECT * FROM testfk;
 ----
 (0 rows)
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 # this insert will fail during commit:
 plpy.execute("INSERT INTO testfk VALUES (0)")
 try:
diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out
index 742988a5b59..dd1ca32fa49 100644
--- a/src/pl/plpython/expected/plpython_trigger.out
+++ b/src/pl/plpython/expected/plpython_trigger.out
@@ -15,20 +15,20 @@ if TD["new"]["fname"] == "william":
 	TD["new"]["fname"] = TD["args"][0]
 	rv = "MODIFY"
 return rv'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION users_update() returns trigger
 	AS
 'if TD["event"] == "UPDATE":
 	if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
 		return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE FUNCTION users_delete() RETURNS trigger
 	AS
 'if TD["old"]["fname"] == TD["args"][0]:
 	return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
 	EXECUTE PROCEDURE users_insert ('willem');
 CREATE TRIGGER users_update_trig BEFORE UPDATE ON users FOR EACH ROW
@@ -71,7 +71,7 @@ CREATE TABLE trigger_test_generated (
 	i int,
         j int GENERATED ALWAYS AS (i * 2) STORED
 );
-CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
 
 if 'relid' in TD:
 	TD['relid'] = "bogus:12345"
@@ -328,7 +328,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid1() RETURNS trigger
 AS $$
     return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger1
 BEFORE INSERT ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid1();
@@ -341,7 +341,7 @@ DROP TRIGGER stupid_trigger1 ON trigger_test;
 CREATE FUNCTION stupid2() RETURNS trigger
 AS $$
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger2
 BEFORE DELETE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid2();
@@ -353,7 +353,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid3() RETURNS trigger
 AS $$
     return "foo"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid3();
@@ -365,8 +365,8 @@ DROP TRIGGER stupid_trigger3 ON trigger_test;
 -- Unicode variant
 CREATE FUNCTION stupid3u() RETURNS trigger
 AS $$
-    return u"foo"
-$$ LANGUAGE plpythonu;
+    return "foo"
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid3u();
@@ -380,7 +380,7 @@ CREATE FUNCTION stupid4() RETURNS trigger
 AS $$
     del TD["new"]
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger4
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid4();
@@ -394,7 +394,7 @@ CREATE FUNCTION stupid5() RETURNS trigger
 AS $$
     TD["new"] = ['foo', 'bar']
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger5
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid5();
@@ -408,7 +408,7 @@ CREATE FUNCTION stupid6() RETURNS trigger
 AS $$
     TD["new"] = {1: 'foo', 2: 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger6
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid6();
@@ -422,7 +422,7 @@ CREATE FUNCTION stupid7() RETURNS trigger
 AS $$
     TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid7();
@@ -434,9 +434,9 @@ DROP TRIGGER stupid_trigger7 ON trigger_test;
 -- Unicode variant
 CREATE FUNCTION stupid7u() RETURNS trigger
 AS $$
-    TD["new"] = {u'v': 'foo', u'a': 'bar'}
+    TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE stupid7u();
@@ -461,7 +461,7 @@ CREATE FUNCTION test_null() RETURNS trigger
 AS $$
     TD["new"]['v'] = None
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER test_null_trigger
 BEFORE UPDATE ON trigger_test
 FOR EACH ROW EXECUTE PROCEDURE test_null();
@@ -481,7 +481,7 @@ SET DateStyle = 'ISO';
 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
 CREATE TRIGGER set_modif_time BEFORE UPDATE ON pb
   FOR EACH ROW EXECUTE PROCEDURE set_modif_time();
@@ -507,7 +507,7 @@ CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
     TD['new']['f1'] = (3, False)
     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
 INSERT INTO composite_trigger_test VALUES (NULL, NULL);
@@ -521,7 +521,7 @@ SELECT * FROM composite_trigger_test;
 CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
 INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL);
@@ -540,7 +540,7 @@ CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer);
 CREATE TABLE composite_trigger_nested_test(c comp3);
 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
 INSERT INTO composite_trigger_nested_test VALUES (NULL);
@@ -555,7 +555,7 @@ SELECT * FROM composite_trigger_nested_test;
 (3 rows)
 
 -- check that using a function as a trigger over two tables works correctly
-CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
     TD["new"]["data"] = '1234'
     return 'MODIFY'
 $$;
@@ -581,7 +581,7 @@ SELECT * FROM b;
 -- check that SQL run in trigger code can see transition tables
 CREATE TABLE transition_table_test (id int, name text);
 INSERT INTO transition_table_test VALUES (1, 'a');
-CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
 $$
     rv = plpy.execute("SELECT * FROM old_table")
     assert(rv.nrows() == 1)
@@ -601,7 +601,7 @@ DROP TABLE transition_table_test;
 DROP FUNCTION transition_table_test_f();
 -- dealing with generated columns
 CREATE FUNCTION generated_test_func1() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 TD['new']['j'] = 5  # not allowed
 return 'MODIFY'
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index 0a2659fe292..a470911c2ec 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -7,23 +7,23 @@
 CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bool(true);
-INFO:  (True, <type 'bool'>)
+INFO:  (True, <class 'bool'>)
  test_type_conversion_bool 
 ---------------------------
  t
 (1 row)
 
 SELECT * FROM test_type_conversion_bool(false);
-INFO:  (False, <type 'bool'>)
+INFO:  (False, <class 'bool'>)
  test_type_conversion_bool 
 ---------------------------
  f
 (1 row)
 
 SELECT * FROM test_type_conversion_bool(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_bool 
 ---------------------------
  
@@ -48,7 +48,7 @@ elif n == 5:
    ret = [0]
 plpy.info(ret, not not ret)
 return ret
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bool_other(0);
 INFO:  (0, False)
  test_type_conversion_bool_other 
@@ -94,16 +94,16 @@ INFO:  ([0], True)
 CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_char('a');
-INFO:  ('a', <type 'str'>)
+INFO:  ('a', <class 'str'>)
  test_type_conversion_char 
 ---------------------------
  a
 (1 row)
 
 SELECT * FROM test_type_conversion_char(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_char 
 ---------------------------
  
@@ -112,23 +112,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int2(100::int2);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int2 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int2(-100::int2);
-INFO:  (-100, <type 'int'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int2 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int2(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int2 
 ---------------------------
                           
@@ -137,23 +137,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int4(100);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int4 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int4(-100);
-INFO:  (-100, <type 'int'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int4 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int4(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int4 
 ---------------------------
                           
@@ -162,30 +162,30 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_int8(100);
-INFO:  (100L, <type 'long'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                        100
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(-100);
-INFO:  (-100L, <type 'long'>)
+INFO:  (-100, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                       -100
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(5000000000);
-INFO:  (5000000000L, <type 'long'>)
+INFO:  (5000000000, <class 'int'>)
  test_type_conversion_int8 
 ---------------------------
                 5000000000
 (1 row)
 
 SELECT * FROM test_type_conversion_int8(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_int8 
 ---------------------------
                           
@@ -196,7 +196,7 @@ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
 # between decimal and cdecimal
 plpy.info(str(x), x.__class__.__name__)
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_numeric(100);
 INFO:  ('100', 'Decimal')
  test_type_conversion_numeric 
@@ -256,30 +256,30 @@ INFO:  ('None', 'NoneType')
 CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_float4(100);
-INFO:  (100.0, <type 'float'>)
+INFO:  (100.0, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                          100
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(-100);
-INFO:  (-100.0, <type 'float'>)
+INFO:  (-100.0, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                         -100
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(5000.5);
-INFO:  (5000.5, <type 'float'>)
+INFO:  (5000.5, <class 'float'>)
  test_type_conversion_float4 
 -----------------------------
                       5000.5
 (1 row)
 
 SELECT * FROM test_type_conversion_float4(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_float4 
 -----------------------------
                             
@@ -288,37 +288,37 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_float8(100);
-INFO:  (100.0, <type 'float'>)
+INFO:  (100.0, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                          100
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(-100);
-INFO:  (-100.0, <type 'float'>)
+INFO:  (-100.0, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                         -100
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(5000000000.5);
-INFO:  (5000000000.5, <type 'float'>)
+INFO:  (5000000000.5, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
                 5000000000.5
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_float8 
 -----------------------------
                             
 (1 row)
 
 SELECT * FROM test_type_conversion_float8(100100100.654321);
-INFO:  (100100100.654321, <type 'float'>)
+INFO:  (100100100.654321, <class 'float'>)
  test_type_conversion_float8 
 -----------------------------
             100100100.654321
@@ -327,23 +327,23 @@ INFO:  (100100100.654321, <type 'float'>)
 CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_oid(100);
-INFO:  (100L, <type 'long'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_oid 
 --------------------------
                       100
 (1 row)
 
 SELECT * FROM test_type_conversion_oid(2147483649);
-INFO:  (2147483649L, <type 'long'>)
+INFO:  (2147483649, <class 'int'>)
  test_type_conversion_oid 
 --------------------------
                2147483649
 (1 row)
 
 SELECT * FROM test_type_conversion_oid(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_oid 
 --------------------------
                          
@@ -352,16 +352,16 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_text('hello world');
-INFO:  ('hello world', <type 'str'>)
+INFO:  ('hello world', <class 'str'>)
  test_type_conversion_text 
 ---------------------------
  hello world
 (1 row)
 
 SELECT * FROM test_type_conversion_text(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_text 
 ---------------------------
  
@@ -370,23 +370,23 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bytea('hello world');
-INFO:  ('hello world', <type 'str'>)
+INFO:  (b'hello world', <class 'bytes'>)
  test_type_conversion_bytea 
 ----------------------------
  \x68656c6c6f20776f726c64
 (1 row)
 
 SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
-INFO:  ('null\x00byte', <type 'str'>)
+INFO:  (b'null\x00byte', <class 'bytes'>)
  test_type_conversion_bytea 
 ----------------------------
  \x6e756c6c0062797465
 (1 row)
 
 SELECT * FROM test_type_conversion_bytea(null);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_bytea 
 ----------------------------
  
@@ -395,14 +395,14 @@ INFO:  (None, <type 'NoneType'>)
 CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
 import marshal
 return marshal.dumps('hello world')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
 import marshal
 try:
     return marshal.loads(x)
 except ValueError as e:
     return 'FAILED: ' + str(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
  test_type_unmarshal 
 ---------------------
@@ -415,7 +415,7 @@ SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
 CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
 CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_booltrue(true, true);
  test_type_conversion_booltrue 
 -------------------------------
@@ -432,21 +432,21 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
 CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
  test_type_conversion_uint2 
 ----------------------------
                          50
 (1 row)
 
 SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
-INFO:  (100, <type 'int'>)
+INFO:  (100, <class 'int'>)
 ERROR:  value for domain uint2 violates check constraint "uint2_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_uint2"
 SELECT * FROM test_type_conversion_uint2(null, 1);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_uint2 
 ----------------------------
                           1
@@ -455,7 +455,7 @@ INFO:  (None, <type 'NoneType'>)
 CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
 CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_nnint(10, 20);
  test_type_conversion_nnint 
 ----------------------------
@@ -472,9 +472,9 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
 CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
-INFO:  ('hello wold', <type 'str'>)
+INFO:  (b'hello wold', <class 'bytes'>)
  test_type_conversion_bytea10 
 ------------------------------
  \x68656c6c6f20776f6c64
@@ -483,14 +483,14 @@ INFO:  ('hello wold', <type 'str'>)
 SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
-INFO:  ('hello word', <type 'str'>)
+INFO:  (b'hello word', <class 'bytes'>)
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_bytea10"
 SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 SELECT * FROM test_type_conversion_bytea10('hello word', null);
-INFO:  ('hello word', <type 'str'>)
+INFO:  (b'hello word', <class 'bytes'>)
 ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_bytea10"
@@ -500,58 +500,58 @@ PL/Python function "test_type_conversion_bytea10"
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
-INFO:  ([0, 100], <type 'list'>)
+INFO:  ([0, 100], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {0,100}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
-INFO:  ([0, -100, 55], <type 'list'>)
+INFO:  ([0, -100, 55], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {0,-100,55}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
-INFO:  ([None, 1], <type 'list'>)
+INFO:  ([None, 1], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {NULL,1}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
-INFO:  ([], <type 'list'>)
+INFO:  ([], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(NULL);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_array_int4 
 ---------------------------------
  
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-INFO:  ([[1, 2, 3], [4, 5, 6]], <type 'list'>)
+INFO:  ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {{1,2,3},{4,5,6}}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <type 'list'>)
+INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
           test_type_conversion_array_int4          
 ---------------------------------------------------
  {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
-INFO:  ([1, 2, 3], <type 'list'>)
+INFO:  ([1, 2, 3], <class 'list'>)
  test_type_conversion_array_int4 
 ---------------------------------
  {1,2,3}
@@ -560,9 +560,9 @@ INFO:  ([1, 2, 3], <type 'list'>)
 CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
-INFO:  ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <type 'list'>)
+INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
           test_type_conversion_array_int8          
 ---------------------------------------------------
  {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
@@ -571,10 +571,10 @@ INFO:  ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <
 CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
             [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
-INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <type 'list'>)
+INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
                                                  test_type_conversion_array_date                                                 
 ---------------------------------------------------------------------------------------------------------------------------------
  {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
@@ -583,12 +583,12 @@ INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']
 CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
             [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
             [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
             ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
-INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <type 'list'>)
+INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
                                                                                                                                                       test_type_conversion_array_timestamp                                                                                                                                                      
 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
@@ -598,9 +598,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemint4(8,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
                                                                                                                                                                                                                                                                              pyreturnmultidemint4                                                                                                                                                                                                                                                                              
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
@@ -610,9 +610,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemint8(5,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
                                                                                                                                                                                    pyreturnmultidemint8                                                                                                                                                                                    
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
@@ -622,9 +622,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemfloat4(6,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
                                                                                                                                                                                                                 pyreturnmultidemfloat4                                                                                                                                                                                                                 
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
@@ -634,9 +634,9 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 select pyreturnmultidemfloat8(7,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <type 'list'>)
+INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
                                                                                                                                                                                                                                               pyreturnmultidemfloat8                                                                                                                                                                                                                                               
 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
@@ -645,16 +645,16 @@ INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6],
 CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
-INFO:  (['foo', 'bar'], <type 'list'>)
+INFO:  (['foo', 'bar'], <class 'list'>)
  test_type_conversion_array_text 
 ---------------------------------
  {foo,bar}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
-INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
+INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
  test_type_conversion_array_text 
 ---------------------------------
  {{foo,bar},{foo2,bar2}}
@@ -663,9 +663,9 @@ INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
 CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
-INFO:  (['\xde\xad\xbe\xef', None], <type 'list'>)
+INFO:  ([b'\xde\xad\xbe\xef', None], <class 'list'>)
  test_type_conversion_array_bytea 
 ----------------------------------
  {"\\xdeadbeef",NULL}
@@ -673,7 +673,7 @@ INFO:  (['\xde\xad\xbe\xef', None], <type 'list'>)
 
 CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_mixed1();
  test_type_conversion_array_mixed1 
 -----------------------------------
@@ -682,14 +682,14 @@ SELECT * FROM test_type_conversion_array_mixed1();
 
 CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_mixed2();
 ERROR:  invalid input syntax for type integer: "abc"
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_array_mixed2"
 CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
 return [[1,2,3],[4,5]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_mdarray_malformed();
 ERROR:  wrong length of inner sequence: has length 2, but 3 was expected
 DETAIL:  To construct a multidimensional array, the inner sequences must all have the same length.
@@ -697,14 +697,14 @@ CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_mdarray_malformed"
 CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
 return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_mdarray_toodeep();
 ERROR:  number of array dimensions exceeds the maximum allowed (6)
 CONTEXT:  while creating return value
 PL/Python function "test_type_conversion_mdarray_toodeep"
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_record();
  test_type_conversion_array_record 
 -----------------------------------
@@ -713,7 +713,7 @@ SELECT * FROM test_type_conversion_array_record();
 
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_string();
  test_type_conversion_array_string 
 -----------------------------------
@@ -722,7 +722,7 @@ SELECT * FROM test_type_conversion_array_string();
 
 CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
 return ('abc', 'def')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_tuple();
  test_type_conversion_array_tuple 
 ----------------------------------
@@ -731,7 +731,7 @@ SELECT * FROM test_type_conversion_array_tuple();
 
 CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
 return 5
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_error();
 ERROR:  return value of function with array return type is not a Python sequence
 CONTEXT:  while creating return value
@@ -743,16 +743,16 @@ CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AN
 CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
-INFO:  ([0, 100], <type 'list'>)
+INFO:  ([0, 100], <class 'list'>)
  test_type_conversion_array_domain 
 -----------------------------------
  {0,100}
 (1 row)
 
 SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
-INFO:  (None, <type 'NoneType'>)
+INFO:  (None, <class 'NoneType'>)
  test_type_conversion_array_domain 
 -----------------------------------
  
@@ -760,7 +760,7 @@ INFO:  (None, <type 'NoneType'>)
 
 CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
 return [2,1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain_check_violation();
 ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
 CONTEXT:  while creating return value
@@ -771,9 +771,9 @@ PL/Python function "test_type_conversion_array_domain_check_violation"
 CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_read_uint2_array(array[1::uint2]);
-INFO:  ([1], <type 'list'>)
+INFO:  ([1], <class 'list'>)
  test_read_uint2_array 
 -----------------------
                      1
@@ -781,7 +781,7 @@ INFO:  ([1], <type 'list'>)
 
 CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_build_uint2_array(1::int2);
  test_build_uint2_array 
 ------------------------
@@ -800,7 +800,7 @@ PL/Python function "test_build_uint2_array"
 CREATE FUNCTION test_type_conversion_domain_array(x integer[])
   RETURNS ordered_pair_domain[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_domain_array(array[2,4]);
 ERROR:  return value of function with array return type is not a Python sequence
 CONTEXT:  while creating return value
@@ -813,9 +813,9 @@ CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
   RETURNS integer AS $$
 plpy.info(x, type(x))
 return x[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_domain_array2(array[2,4]);
-INFO:  ([2, 4], <type 'list'>)
+INFO:  ([2, 4], <class 'list'>)
  test_type_conversion_domain_array2 
 ------------------------------------
                                   4
@@ -827,9 +827,9 @@ CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
   RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
-INFO:  ([[2, 4]], <type 'list'>)
+INFO:  ([[2, 4]], <class 'list'>)
  test_type_conversion_array_domain_array 
 -----------------------------------------
  {2,4}
@@ -846,7 +846,7 @@ CREATE TABLE employee (
 INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
 CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
 return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT name, test_composite_table_input(employee.*) FROM employee;
  name | test_composite_table_input 
 ------+----------------------------
@@ -876,7 +876,7 @@ CREATE TYPE named_pair AS (
 );
 CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
 return sum(p.values())
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT test_composite_type_input(row(1, 2));
  test_composite_type_input 
 ---------------------------
@@ -896,7 +896,7 @@ SELECT test_composite_type_input(row(1, 2));
 CREATE TYPE nnint_container AS (f1 int, f2 nnint);
 CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
 return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT nnint_test(null, 3);
  nnint_test 
 ------------
@@ -913,7 +913,7 @@ PL/Python function "nnint_test"
 CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
 CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
 return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT read_ordered_named_pair(row(1, 2));
  read_ordered_named_pair 
 -------------------------
@@ -924,7 +924,7 @@ SELECT read_ordered_named_pair(row(2, 1));  -- fail
 ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
 CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
 return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT build_ordered_named_pair(1,2);
  build_ordered_named_pair 
 --------------------------
@@ -937,7 +937,7 @@ CONTEXT:  while creating return value
 PL/Python function "build_ordered_named_pair"
 CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
 return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT build_ordered_named_pairs(1,2);
  build_ordered_named_pairs 
 ---------------------------
@@ -952,7 +952,7 @@ PL/Python function "build_ordered_named_pairs"
 -- Prepared statements
 --
 CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
 rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
@@ -965,7 +965,7 @@ SELECT test_prep_bool_input(); -- 1
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
 rv = plpy.execute(plan, [0], 5)
@@ -980,7 +980,7 @@ INFO:  {'val': False}
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
 rv = plpy.execute(plan, [bb], 5)
@@ -993,7 +993,7 @@ SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated
 (1 row)
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
 rv = plpy.execute(plan, [], 5)
@@ -1001,7 +1001,7 @@ plpy.info(rv[0])
 return rv[0]['val']
 $$;
 SELECT test_prep_bytea_output();
-INFO:  {'val': '\xaa\x00\xbb'}
+INFO:  {'val': b'\xaa\x00\xbb'}
  test_prep_bytea_output 
 ------------------------
  \xaa00bb
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
deleted file mode 100644
index a6ec10d5e18..00000000000
--- a/src/pl/plpython/expected/plpython_types_3.out
+++ /dev/null
@@ -1,1009 +0,0 @@
---
--- Test data type behavior
---
---
--- Base/common types
---
-CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bool(true);
-INFO:  (True, <class 'bool'>)
- test_type_conversion_bool 
----------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool(false);
-INFO:  (False, <class 'bool'>)
- test_type_conversion_bool 
----------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_bool 
----------------------------
- 
-(1 row)
-
--- test various other ways to express Booleans in Python
-CREATE FUNCTION test_type_conversion_bool_other(n int) RETURNS bool AS $$
-# numbers
-if n == 0:
-   ret = 0
-elif n == 1:
-   ret = 5
-# strings
-elif n == 2:
-   ret = ''
-elif n == 3:
-   ret = 'fa' # true in Python, false in PostgreSQL
-# containers
-elif n == 4:
-   ret = []
-elif n == 5:
-   ret = [0]
-plpy.info(ret, not not ret)
-return ret
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bool_other(0);
-INFO:  (0, False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(1);
-INFO:  (5, True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(2);
-INFO:  ('', False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(3);
-INFO:  ('fa', True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(4);
-INFO:  ([], False)
- test_type_conversion_bool_other 
----------------------------------
- f
-(1 row)
-
-SELECT * FROM test_type_conversion_bool_other(5);
-INFO:  ([0], True)
- test_type_conversion_bool_other 
----------------------------------
- t
-(1 row)
-
-CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_char('a');
-INFO:  ('a', <class 'str'>)
- test_type_conversion_char 
----------------------------
- a
-(1 row)
-
-SELECT * FROM test_type_conversion_char(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_char 
----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int2(100::int2);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int2 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int2(-100::int2);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int2 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int2(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int2 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int4(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int4 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int4(-100);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int4 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int4(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int4 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_int8(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                       100
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(-100);
-INFO:  (-100, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                      -100
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(5000000000);
-INFO:  (5000000000, <class 'int'>)
- test_type_conversion_int8 
----------------------------
-                5000000000
-(1 row)
-
-SELECT * FROM test_type_conversion_int8(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_int8 
----------------------------
-                          
-(1 row)
-
-CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
-# print just the class name, not the type, to avoid differences
-# between decimal and cdecimal
-plpy.info(str(x), x.__class__.__name__)
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_numeric(100);
-INFO:  ('100', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                          100
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(-100);
-INFO:  ('-100', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                         -100
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(100.0);
-INFO:  ('100.0', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                        100.0
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(100.00);
-INFO:  ('100.00', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                       100.00
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(5000000000.5);
-INFO:  ('5000000000.5', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-                 5000000000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(1234567890.0987654321);
-INFO:  ('1234567890.0987654321', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-        1234567890.0987654321
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(-1234567890.0987654321);
-INFO:  ('-1234567890.0987654321', 'Decimal')
- test_type_conversion_numeric 
-------------------------------
-       -1234567890.0987654321
-(1 row)
-
-SELECT * FROM test_type_conversion_numeric(null);
-INFO:  ('None', 'NoneType')
- test_type_conversion_numeric 
-------------------------------
-                             
-(1 row)
-
-CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_float4(100);
-INFO:  (100.0, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                         100
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(-100);
-INFO:  (-100.0, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                        -100
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(5000.5);
-INFO:  (5000.5, <class 'float'>)
- test_type_conversion_float4 
------------------------------
-                      5000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_float4(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_float4 
------------------------------
-                            
-(1 row)
-
-CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_float8(100);
-INFO:  (100.0, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                         100
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(-100);
-INFO:  (-100.0, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                        -100
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(5000000000.5);
-INFO:  (5000000000.5, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-                5000000000.5
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_float8 
------------------------------
-                            
-(1 row)
-
-SELECT * FROM test_type_conversion_float8(100100100.654321);
-INFO:  (100100100.654321, <class 'float'>)
- test_type_conversion_float8 
------------------------------
-            100100100.654321
-(1 row)
-
-CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_oid(100);
-INFO:  (100, <class 'int'>)
- test_type_conversion_oid 
---------------------------
-                      100
-(1 row)
-
-SELECT * FROM test_type_conversion_oid(2147483649);
-INFO:  (2147483649, <class 'int'>)
- test_type_conversion_oid 
---------------------------
-               2147483649
-(1 row)
-
-SELECT * FROM test_type_conversion_oid(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_oid 
---------------------------
-                         
-(1 row)
-
-CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_text('hello world');
-INFO:  ('hello world', <class 'str'>)
- test_type_conversion_text 
----------------------------
- hello world
-(1 row)
-
-SELECT * FROM test_type_conversion_text(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_text 
----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bytea('hello world');
-INFO:  (b'hello world', <class 'bytes'>)
- test_type_conversion_bytea 
-----------------------------
- \x68656c6c6f20776f726c64
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
-INFO:  (b'null\x00byte', <class 'bytes'>)
- test_type_conversion_bytea 
-----------------------------
- \x6e756c6c0062797465
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea(null);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_bytea 
-----------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
-import marshal
-return marshal.dumps('hello world')
-$$ LANGUAGE plpython3u;
-CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
-import marshal
-try:
-    return marshal.loads(x)
-except ValueError as e:
-    return 'FAILED: ' + str(e)
-$$ LANGUAGE plpython3u;
-SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
- test_type_unmarshal 
----------------------
- hello world
-(1 row)
-
---
--- Domains
---
-CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
-CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_booltrue(true, true);
- test_type_conversion_booltrue 
--------------------------------
- t
-(1 row)
-
-SELECT * FROM test_type_conversion_booltrue(false, true);
-ERROR:  value for domain booltrue violates check constraint "booltrue_check"
-SELECT * FROM test_type_conversion_booltrue(true, false);
-ERROR:  value for domain booltrue violates check constraint "booltrue_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_booltrue"
-CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
-CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
-plpy.info(x, type(x))
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
-INFO:  (100, <class 'int'>)
- test_type_conversion_uint2 
-----------------------------
-                         50
-(1 row)
-
-SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
-INFO:  (100, <class 'int'>)
-ERROR:  value for domain uint2 violates check constraint "uint2_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_uint2"
-SELECT * FROM test_type_conversion_uint2(null, 1);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_uint2 
-----------------------------
-                          1
-(1 row)
-
-CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
-CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_nnint(10, 20);
- test_type_conversion_nnint 
-----------------------------
-                         20
-(1 row)
-
-SELECT * FROM test_type_conversion_nnint(null, 20);
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-SELECT * FROM test_type_conversion_nnint(10, null);
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_nnint"
-CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL);
-CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
-plpy.info(x, type(x))
-return y
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
-INFO:  (b'hello wold', <class 'bytes'>)
- test_type_conversion_bytea10 
-------------------------------
- \x68656c6c6f20776f6c64
-(1 row)
-
-SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
-INFO:  (b'hello word', <class 'bytes'>)
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_bytea10"
-SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-SELECT * FROM test_type_conversion_bytea10('hello word', null);
-INFO:  (b'hello word', <class 'bytes'>)
-ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_bytea10"
---
--- Arrays
---
-CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
-INFO:  ([0, 100], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {0,100}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
-INFO:  ([0, -100, 55], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {0,-100,55}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
-INFO:  ([None, 1], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {NULL,1}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
-INFO:  ([], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(NULL);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_array_int4 
----------------------------------
- 
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-INFO:  ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {{1,2,3},{4,5,6}}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
-          test_type_conversion_array_int4          
----------------------------------------------------
- {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
-INFO:  ([1, 2, 3], <class 'list'>)
- test_type_conversion_array_int4 
----------------------------------
- {1,2,3}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
-INFO:  ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
-          test_type_conversion_array_int8          
----------------------------------------------------
- {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
-            [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
-INFO:  ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
-                                                 test_type_conversion_array_date                                                 
----------------------------------------------------------------------------------------------------------------------------------
- {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
-            [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
-            [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
-            ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
-INFO:  ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
-                                                                                                                                                      test_type_conversion_array_timestamp                                                                                                                                                      
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 ) RETURNS int4[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemint4(8,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
-                                                                                                                                                                                                                                                                             pyreturnmultidemint4                                                                                                                                                                                                                                                                              
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 ) RETURNS int8[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemint8(5,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
-                                                                                                                                                                                   pyreturnmultidemint8                                                                                                                                                                                    
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4 ) RETURNS float4[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemfloat4(6,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
-                                                                                                                                                                                                                pyreturnmultidemfloat4                                                                                                                                                                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
-(1 row)
-
-CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4 ) RETURNS float8[] AS $BODY$
-m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
-plpy.info(m, type(m))
-return m
-$BODY$ LANGUAGE plpython3u;
-select pyreturnmultidemfloat8(7,5,3,2);
-INFO:  ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
-                                                                                                                                                                                                                                              pyreturnmultidemfloat8                                                                                                                                                                                                                                               
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
-INFO:  (['foo', 'bar'], <class 'list'>)
- test_type_conversion_array_text 
----------------------------------
- {foo,bar}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
-INFO:  ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
- test_type_conversion_array_text 
----------------------------------
- {{foo,bar},{foo2,bar2}}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
-INFO:  ([b'\xde\xad\xbe\xef', None], <class 'list'>)
- test_type_conversion_array_bytea 
-----------------------------------
- {"\\xdeadbeef",NULL}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
-return [123, 'abc']
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_mixed1();
- test_type_conversion_array_mixed1 
------------------------------------
- {123,abc}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
-return [123, 'abc']
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_mixed2();
-ERROR:  invalid input syntax for type integer: "abc"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_mixed2"
-CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
-return [[1,2,3],[4,5]]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_mdarray_malformed();
-ERROR:  wrong length of inner sequence: has length 2, but 3 was expected
-DETAIL:  To construct a multidimensional array, the inner sequences must all have the same length.
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_mdarray_malformed"
-CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
-return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_mdarray_toodeep();
-ERROR:  number of array dimensions exceeds the maximum allowed (6)
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_mdarray_toodeep"
-CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
-return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_record();
- test_type_conversion_array_record 
------------------------------------
- {"(one,42)","(two,11)"}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
-return 'abc'
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_string();
- test_type_conversion_array_string 
------------------------------------
- {a,b,c}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
-return ('abc', 'def')
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_tuple();
- test_type_conversion_array_tuple 
-----------------------------------
- {abc,def}
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
-return 5
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_error();
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_error"
---
--- Domains over arrays
---
-CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AND VALUE[1] < VALUE[2]);
-CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
-plpy.info(x, type(x))
-return x
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
-INFO:  ([0, 100], <class 'list'>)
- test_type_conversion_array_domain 
------------------------------------
- {0,100}
-(1 row)
-
-SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
-INFO:  (None, <class 'NoneType'>)
- test_type_conversion_array_domain 
------------------------------------
- 
-(1 row)
-
-CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
-return [2,1]
-$$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_domain_check_violation();
-ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_array_domain_check_violation"
---
--- Arrays of domains
---
-CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
-plpy.info(x, type(x))
-return x[0]
-$$ LANGUAGE plpythonu;
-select test_read_uint2_array(array[1::uint2]);
-INFO:  ([1], <class 'list'>)
- test_read_uint2_array 
------------------------
-                     1
-(1 row)
-
-CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
-return [x, x]
-$$ LANGUAGE plpythonu;
-select test_build_uint2_array(1::int2);
- test_build_uint2_array 
-------------------------
- {1,1}
-(1 row)
-
-select test_build_uint2_array(-1::int2);  -- fail
-ERROR:  value for domain uint2 violates check constraint "uint2_check"
-CONTEXT:  while creating return value
-PL/Python function "test_build_uint2_array"
---
--- ideally this would work, but for now it doesn't, because the return value
--- is [[2,4], [2,4]] which our conversion code thinks should become a 2-D
--- integer array, not an array of arrays.
---
-CREATE FUNCTION test_type_conversion_domain_array(x integer[])
-  RETURNS ordered_pair_domain[] AS $$
-return [x, x]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_domain_array(array[2,4]);
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_domain_array"
-select test_type_conversion_domain_array(array[4,2]);  -- fail
-ERROR:  return value of function with array return type is not a Python sequence
-CONTEXT:  while creating return value
-PL/Python function "test_type_conversion_domain_array"
-CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
-  RETURNS integer AS $$
-plpy.info(x, type(x))
-return x[1]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_domain_array2(array[2,4]);
-INFO:  ([2, 4], <class 'list'>)
- test_type_conversion_domain_array2 
-------------------------------------
-                                  4
-(1 row)
-
-select test_type_conversion_domain_array2(array[4,2]);  -- fail
-ERROR:  value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
-CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
-  RETURNS ordered_pair_domain AS $$
-plpy.info(x, type(x))
-return x[0]
-$$ LANGUAGE plpythonu;
-select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
-INFO:  ([[2, 4]], <class 'list'>)
- test_type_conversion_array_domain_array 
------------------------------------------
- {2,4}
-(1 row)
-
----
---- Composite types
----
-CREATE TABLE employee (
-    name text,
-    basesalary integer,
-    bonus integer
-);
-INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
-CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
-return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpython3u;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
- name | test_composite_table_input 
-------+----------------------------
- John |                        110
- Mary |                        210
-(2 rows)
-
-ALTER TABLE employee DROP bonus;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
-ERROR:  KeyError: 'bonus'
-CONTEXT:  Traceback (most recent call last):
-  PL/Python function "test_composite_table_input", line 2, in <module>
-    return e['basesalary'] + e['bonus']
-PL/Python function "test_composite_table_input"
-ALTER TABLE employee ADD bonus integer;
-UPDATE employee SET bonus = 10;
-SELECT name, test_composite_table_input(employee.*) FROM employee;
- name | test_composite_table_input 
-------+----------------------------
- John |                        110
- Mary |                        210
-(2 rows)
-
-CREATE TYPE named_pair AS (
-    i integer,
-    j integer
-);
-CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
-return sum(p.values())
-$$ LANGUAGE plpython3u;
-SELECT test_composite_type_input(row(1, 2));
- test_composite_type_input 
----------------------------
-                         3
-(1 row)
-
-ALTER TYPE named_pair RENAME TO named_pair_2;
-SELECT test_composite_type_input(row(1, 2));
- test_composite_type_input 
----------------------------
-                         3
-(1 row)
-
---
--- Domains within composite
---
-CREATE TYPE nnint_container AS (f1 int, f2 nnint);
-CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
-return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
-SELECT nnint_test(null, 3);
- nnint_test 
-------------
- (,3)
-(1 row)
-
-SELECT nnint_test(3, null);  -- fail
-ERROR:  value for domain nnint violates check constraint "nnint_check"
-CONTEXT:  while creating return value
-PL/Python function "nnint_test"
---
--- Domains of composite
---
-CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
-CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
-return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
-SELECT read_ordered_named_pair(row(1, 2));
- read_ordered_named_pair 
--------------------------
-                       3
-(1 row)
-
-SELECT read_ordered_named_pair(row(2, 1));  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
-return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
-SELECT build_ordered_named_pair(1,2);
- build_ordered_named_pair 
---------------------------
- (1,2)
-(1 row)
-
-SELECT build_ordered_named_pair(2,1);  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CONTEXT:  while creating return value
-PL/Python function "build_ordered_named_pair"
-CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
-return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
-SELECT build_ordered_named_pairs(1,2);
- build_ordered_named_pairs 
----------------------------
- {"(1,2)","(1,3)"}
-(1 row)
-
-SELECT build_ordered_named_pairs(2,1);  -- fail
-ERROR:  value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
-CONTEXT:  while creating return value
-PL/Python function "build_ordered_named_pairs"
---
--- Prepared statements
---
-CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
-rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
-return rv[0]['val']
-$$;
-SELECT test_prep_bool_input(); -- 1
- test_prep_bool_input 
-----------------------
-                    1
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
-rv = plpy.execute(plan, [0], 5)
-plpy.info(rv[0])
-return rv[0]['val']
-$$;
-SELECT test_prep_bool_output(); -- false
-INFO:  {'val': False}
- test_prep_bool_output 
------------------------
- f
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
-rv = plpy.execute(plan, [bb], 5)
-return rv[0]['val']
-$$;
-SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value)
- test_prep_bytea_input 
------------------------
-                     3
-(1 row)
-
-CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpython3u
-AS $$
-plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
-rv = plpy.execute(plan, [], 5)
-plpy.info(rv[0])
-return rv[0]['val']
-$$;
-SELECT test_prep_bytea_output();
-INFO:  {'val': b'\xaa\x00\xbb'}
- test_prep_bytea_output 
-------------------------
- \xaa00bb
-(1 row)
-
diff --git a/src/pl/plpython/expected/plpython_unicode.out b/src/pl/plpython/expected/plpython_unicode.out
index c7546dd4587..fd54b0b88e8 100644
--- a/src/pl/plpython/expected/plpython_unicode.out
+++ b/src/pl/plpython/expected/plpython_unicode.out
@@ -11,24 +11,24 @@ CREATE TABLE unicode_test (
 	testvalue  text NOT NULL
 );
 CREATE FUNCTION unicode_return() RETURNS text AS E'
-return u"\\xA0"
-' LANGUAGE plpythonu;
+return "\\xA0"
+' LANGUAGE plpython3u;
 CREATE FUNCTION unicode_trigger() RETURNS trigger AS E'
-TD["new"]["testvalue"] = u"\\xA0"
+TD["new"]["testvalue"] = "\\xA0"
 return "MODIFY"
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test
   FOR EACH ROW EXECUTE PROCEDURE unicode_trigger();
 CREATE FUNCTION unicode_plan1() RETURNS text AS E'
 plan = plpy.prepare("SELECT $1 AS testvalue", ["text"])
-rv = plpy.execute(plan, [u"\\xA0"], 1)
+rv = plpy.execute(plan, ["\\xA0"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 CREATE FUNCTION unicode_plan2() RETURNS text AS E'
-plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"])
+plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"])
 rv = plpy.execute(plan, ["foo", "bar"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 SELECT unicode_return();
  unicode_return 
 ----------------
diff --git a/src/pl/plpython/expected/plpython_void.out b/src/pl/plpython/expected/plpython_void.out
index 1080d12d6b2..07d0760783e 100644
--- a/src/pl/plpython/expected/plpython_void.out
+++ b/src/pl/plpython/expected/plpython_void.out
@@ -3,14 +3,14 @@
 --
 CREATE FUNCTION test_void_func1() RETURNS void AS $$
 x = 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- illegal: can't return non-None value in void-returning func
 CREATE FUNCTION test_void_func2() RETURNS void AS $$
 return 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 CREATE FUNCTION test_return_none() RETURNS int AS $$
 None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 -- Tests for functions returning void
 SELECT test_void_func1(), test_void_func1() IS NULL AS "is null";
  test_void_func1 | is null 
diff --git a/src/pl/plpython/regress-python3-mangle.mk b/src/pl/plpython/regress-python3-mangle.mk
deleted file mode 100644
index a785818a172..00000000000
--- a/src/pl/plpython/regress-python3-mangle.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-ifeq ($(python_majorversion),3)
-# Adjust regression tests for Python 3 compatibility
-#
-# Mention those regression test files that need to be mangled in the
-# variable REGRESS_PLPYTHON3_MANGLE.  They will be copied to a
-# subdirectory python3/ and have their Python syntax and other bits
-# adjusted to work with Python 3.
-
-# Note that the order of the tests needs to be preserved in this
-# expression.
-REGRESS := $(foreach test,$(REGRESS),$(if $(filter $(test),$(REGRESS_PLPYTHON3_MANGLE)),python3/$(test),$(test)))
-
-.PHONY: pgregress-python3-mangle
-pgregress-python3-mangle:
-	$(MKDIR_P) sql/python3 expected/python3 results/python3
-	for file in $(patsubst %,$(srcdir)/sql/%.sql,$(REGRESS_PLPYTHON3_MANGLE)) $(patsubst %,$(srcdir)/expected/%*.out,$(REGRESS_PLPYTHON3_MANGLE)); do \
-	  sed \
-	      -e "s/<type 'exceptions\.\([[:alpha:]]*\)'>/<class '\1'>/g" \
-	      -e "s/<type 'long'>/<class 'int'>/g" \
-	      -e "s/\([0-9][0-9]*\)L/\1/g" \
-	      -e 's/\([ [{]\)u"/\1"/g' \
-	      -e "s/\([ [{]\)u'/\1'/g" \
-	      -e "s/def next/def __next__/g" \
-	      -e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
-	      -e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
-	      -e "s/EXTENSION plpythonu/EXTENSION plpython3u/g" \
-	      -e "s/EXTENSION plpython2u/EXTENSION plpython3u/g" \
-	      -e "s/EXTENSION \([^ ]*\)_plpythonu/EXTENSION \1_plpython3u/g" \
-	      -e "s/EXTENSION \([^ ]*\)_plpython2u/EXTENSION \1_plpython3u/g" \
-	      -e 's/installing required extension "plpython2u"/installing required extension "plpython3u"/g' \
-	    $$file >`echo $$file | sed 's,^.*/\([^/][^/]*/\)\([^/][^/]*\)$$,\1python3/\2,'` || exit; \
-	done
-
-check installcheck: pgregress-python3-mangle
-
-pg_regress_clean_files += sql/python3/ expected/python3/ results/python3/
-
-endif # Python 3
diff --git a/src/pl/plpython/sql/plpython_call.sql b/src/pl/plpython/sql/plpython_call.sql
index b0b3705ae3c..daa4bc377d7 100644
--- a/src/pl/plpython/sql/plpython_call.sql
+++ b/src/pl/plpython/sql/plpython_call.sql
@@ -3,7 +3,7 @@
 --
 
 CREATE PROCEDURE test_proc1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 pass
 $$;
@@ -13,7 +13,7 @@ CALL test_proc1();
 
 -- error: can't return non-None
 CREATE PROCEDURE test_proc2()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return 5
 $$;
@@ -24,7 +24,7 @@ CALL test_proc2();
 CREATE TABLE test1 (a int);
 
 CREATE PROCEDURE test_proc3(x int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("INSERT INTO test1 VALUES (%s)" % x)
 $$;
@@ -37,7 +37,7 @@ SELECT * FROM test1;
 -- output arguments
 
 CREATE PROCEDURE test_proc5(INOUT a text)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return [a + '+' + a]
 $$;
@@ -46,7 +46,7 @@ CALL test_proc5('abc');
 
 
 CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 return (b * a, c * a)
 $$;
@@ -57,7 +57,7 @@ CALL test_proc6(2, 3, 4);
 -- OUT parameters
 
 CREATE PROCEDURE test_proc9(IN a int, OUT b int)
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.notice("a: %s" % (a))
 return (a * 2,)
diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql
index 0fd2f5d5e3b..21757701cc1 100644
--- a/src/pl/plpython/sql/plpython_composite.sql
+++ b/src/pl/plpython/sql/plpython_composite.sql
@@ -1,6 +1,6 @@
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT multiout_simple();
 SELECT * FROM multiout_simple();
@@ -9,7 +9,7 @@ SELECT (multiout_simple()).j + 3;
 
 CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT multiout_simple_setof();
 SELECT * FROM multiout_simple_setof();
@@ -34,7 +34,7 @@ elif typ == 'obj':
     return type_record
 elif typ == 'str':
     return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
 SELECT multiout_record_as('dict', 'foo', 1, 'f');
@@ -77,7 +77,7 @@ for i in range(n):
     power = 2 ** i
     length = plpy.execute("select length('%d')" % power)[0]['length']
     yield power, length
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_setof(3);
 SELECT multiout_setof(5);
@@ -86,7 +86,7 @@ CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$
 return [{'x': 4, 'y' :'four'},
         {'x': 7, 'y' :'seven'},
         {'x': 0, 'y' :'zero'}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_return_table();
 
@@ -94,18 +94,18 @@ CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS
 yield [[1], 'a']
 yield [[1,2], 'b']
 yield [[1,2,3], None]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_array();
 
 CREATE FUNCTION singleout_composite(OUT type_record) AS $$
 return {'first': 1, 'second': 2}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$
 return [{'first': 1, 'second': 2},
        {'first': 3, 'second': 4	}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM singleout_composite();
 SELECT * FROM multiout_composite();
@@ -113,7 +113,7 @@ SELECT * FROM multiout_composite();
 -- composite OUT parameters in functions returning RECORD not supported yet
 CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$
 return (n, (n * 2, n * 3))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$
 if returnnull:
@@ -132,7 +132,7 @@ elif typ == 'str':
     d = "(%r,%r)" % (n * 2, n * 3)
 for i in range(n):
     yield (i, d)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_composite(2);
 SELECT * FROM multiout_table_type_setof('dict', 'f', 3);
@@ -157,7 +157,7 @@ CREATE TABLE changing (
 CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$
 return [(1, {'i': 1, 'j': 2}),
         (1, (3, 4))]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM changing_test();
 ALTER TABLE changing DROP COLUMN j;
@@ -178,14 +178,14 @@ yield {'tab': [('first', 1), ('second', 2)],
 yield {'tab': [('first', 1), ('second', 2)],
       'typ': [{'first': 'third', 'second': 3},
               {'first': 'fourth', 'second': 4}]}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM composite_types_table();
 
 -- check what happens if the output record descriptor changes
 CREATE FUNCTION return_record(t text) RETURNS record AS $$
 return {'t': t, 'val': 10}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM return_record('abc') AS r(t text, val integer);
 SELECT * FROM return_record('abc') AS r(t text, val bigint);
@@ -196,7 +196,7 @@ SELECT * FROM return_record('999') AS r(val text, t integer);
 
 CREATE FUNCTION return_record_2(t text) RETURNS record AS $$
 return {'v1':1,'v2':2,t:3}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int);
 SELECT * FROM return_record_2('v3') AS (v2 int, v3 int, v1 int);
@@ -211,7 +211,7 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
 -- multi-dimensional array of composite types.
 CREATE FUNCTION composite_type_as_list()  RETURNS type_record[] AS $$
   return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list();
 
 -- Starting with PostgreSQL 10, a composite type in an array cannot be
@@ -220,5 +220,5 @@ SELECT * FROM composite_type_as_list();
 -- on the issue.
 CREATE FUNCTION composite_type_as_list_broken()  RETURNS type_record[] AS $$
   return [['first', 1]];
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM composite_type_as_list_broken();
diff --git a/src/pl/plpython/sql/plpython_do.sql b/src/pl/plpython/sql/plpython_do.sql
index 0e281a08ee8..d49413268e9 100644
--- a/src/pl/plpython/sql/plpython_do.sql
+++ b/src/pl/plpython/sql/plpython_do.sql
@@ -1,5 +1,3 @@
-DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
+DO $$ plpy.notice("This is plpython3u.") $$ LANGUAGE plpython3u;
 
-DO $$ plpy.notice("This is plpython2u.") $$ LANGUAGE plpython2u;
-
-DO $$ raise Exception("error test") $$ LANGUAGE plpythonu;
+DO $$ raise Exception("error test") $$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/sql/plpython_drop.sql b/src/pl/plpython/sql/plpython_drop.sql
index 72d5d657ec3..e4f373b2bc7 100644
--- a/src/pl/plpython/sql/plpython_drop.sql
+++ b/src/pl/plpython/sql/plpython_drop.sql
@@ -3,6 +3,4 @@
 --
 SET client_min_messages = WARNING;
 
-DROP EXTENSION plpythonu CASCADE;
-
-DROP EXTENSION IF EXISTS plpython2u CASCADE;
+DROP EXTENSION plpython3u CASCADE;
diff --git a/src/pl/plpython/sql/plpython_ereport.sql b/src/pl/plpython/sql/plpython_ereport.sql
index 58df2057ef5..d4f6223e59d 100644
--- a/src/pl/plpython/sql/plpython_ereport.sql
+++ b/src/pl/plpython/sql/plpython_ereport.sql
@@ -17,28 +17,28 @@ plpy.info('This is message text.',
 plpy.notice('notice', detail='some detail')
 plpy.warning('warning', detail='some detail')
 plpy.error('stop on error', detail='some detail', hint='some hint')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT elog_test();
 
-DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpythonu;
+DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u;
 
 DO $$
 import time;
 from datetime import date
 plpy.info('other types', detail=date(2016, 2, 26))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 plpy.info('other types', detail=basket)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- should fail
-DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
-DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
+DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u;
+DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u;
 
 -- raise exception in python, handle exception in plgsql
 CREATE OR REPLACE FUNCTION raise_exception(_message text, _detail text DEFAULT NULL, _hint text DEFAULT NULL,
@@ -57,7 +57,7 @@ kwargs = {
 }
 # ignore None values
 plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT raise_exception('hello', 'world');
 SELECT raise_exception('message text', 'detail text', _sqlstate => 'YY333');
@@ -118,17 +118,13 @@ BEGIN
 END;
 $$;
 
--- The displayed context is different between Python2 and Python3,
--- but that's not important for this test.
-\set SHOW_CONTEXT never
-
 DO $$
 try:
     plpy.execute("select raise_exception(_message => 'my message', _sqlstate => 'XX987', _hint => 'some hint', _table_name => 'users_tab', _datatype_name => 'user_type')")
 except Exception as e:
     plpy.info(e.spidata)
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 try:
@@ -136,4 +132,4 @@ try:
 except Exception as e:
     plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name))
     raise e
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index 88d6936fd0d..11f14ec5a7c 100644
--- a/src/pl/plpython/sql/plpython_error.sql
+++ b/src/pl/plpython/sql/plpython_error.sql
@@ -7,7 +7,7 @@
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 /* With check_function_bodies = false the function should get defined
  * and the error reported when called
@@ -17,7 +17,7 @@ SET check_function_bodies = false;
 CREATE FUNCTION python_syntax_error() RETURNS text
         AS
 '.syntaxerror'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 SELECT python_syntax_error();
 /* Run the function twice to check if the hashtable entry gets cleaned up */
@@ -30,7 +30,7 @@ RESET check_function_bodies;
 CREATE FUNCTION sql_syntax_error() RETURNS text
         AS
 'plpy.execute("syntax error")'
-        LANGUAGE plpythonu;
+        LANGUAGE plpython3u;
 
 SELECT sql_syntax_error();
 
@@ -40,7 +40,7 @@ SELECT sql_syntax_error();
 CREATE FUNCTION exception_index_invalid(text) RETURNS text
 	AS
 'return args[1]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT exception_index_invalid('test');
 
@@ -51,7 +51,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text
 	AS
 'rv = plpy.execute("SELECT test5(''foo'')")
 return rv[0]'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT exception_index_invalid_nested();
 
@@ -68,7 +68,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_uncaught('rick');
 
@@ -90,7 +90,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_caught('rick');
 
@@ -111,7 +111,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT invalid_type_reraised('rick');
 
@@ -127,7 +127,7 @@ if len(rv):
 	return rv[0]["fname"]
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT valid_type('rick');
 
@@ -147,7 +147,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_error();
 
@@ -167,7 +167,7 @@ def fun3():
 fun3()
 return "not reached"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_error_raise();
 
@@ -187,7 +187,7 @@ def fun3():
 fun3()
 return "you''ve been warned"
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 SELECT nested_warning();
 
@@ -196,7 +196,7 @@ SELECT nested_warning();
 CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
 $$
 plpy.nonexistent
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT toplevel_attribute_error();
 
@@ -213,7 +213,7 @@ def third():
   plpy.execute("select sql_error()")
 
 first()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
 begin
@@ -229,7 +229,7 @@ $$ LANGUAGE plpgsql;
 
 CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
 plpy.execute("select sql_error()")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT python_traceback();
 SELECT sql_error();
@@ -251,7 +251,7 @@ except spiexceptions.NotNullViolation as e:
     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
 except spiexceptions.UniqueViolation as e:
     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT specific_exception(2);
 SELECT specific_exception(NULL);
@@ -262,7 +262,7 @@ SELECT specific_exception(2);
 CREATE FUNCTION python_unique_violation() RETURNS void AS $$
 plpy.execute("insert into specific values (1)")
 plpy.execute("insert into specific values (1)")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
 begin
@@ -283,7 +283,7 @@ CREATE FUNCTION manual_subxact() RETURNS void AS $$
 plpy.execute("savepoint save")
 plpy.execute("create table foo(x integer)")
 plpy.execute("rollback to save")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT manual_subxact();
 
@@ -295,7 +295,7 @@ rollback = plpy.prepare("rollback to save")
 plpy.execute(save)
 plpy.execute("create table foo(x integer)")
 plpy.execute(rollback)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT manual_subxact_prepared();
 
@@ -303,7 +303,7 @@ SELECT manual_subxact_prepared();
  */
 CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
 raise plpy.spiexceptions.DivisionByZero()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 BEGIN
@@ -319,7 +319,7 @@ CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
 exc = plpy.spiexceptions.DivisionByZero()
 exc.sqlstate = 'SILLY'
 raise exc
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 DO $$
 BEGIN
@@ -332,14 +332,14 @@ $$ LANGUAGE plpgsql;
 /* test the context stack trace for nested execution levels
  */
 CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
 plpy.execute("SELECT notice_innerfunc()")
 return 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 \set SHOW_CONTEXT always
 
diff --git a/src/pl/plpython/sql/plpython_global.sql b/src/pl/plpython/sql/plpython_global.sql
index 32502b41eee..96d20492861 100644
--- a/src/pl/plpython/sql/plpython_global.sql
+++ b/src/pl/plpython/sql/plpython_global.sql
@@ -9,7 +9,7 @@ CREATE FUNCTION global_test_one() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_one"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 CREATE FUNCTION global_test_two() returns text
     AS
@@ -18,7 +18,7 @@ CREATE FUNCTION global_test_two() returns text
 if "global_test" not in GD:
 	GD["global_test"] = "set by global_test_two"
 return "SD: " + SD["global_test"] + ", GD: " + GD["global_test"]'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 CREATE FUNCTION static_test() returns int4
@@ -29,7 +29,7 @@ else:
 	SD["call"] = 1
 return SD["call"]
 '
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 SELECT static_test();
diff --git a/src/pl/plpython/sql/plpython_import.sql b/src/pl/plpython/sql/plpython_import.sql
index ec887677e1e..3031eef2e69 100644
--- a/src/pl/plpython/sql/plpython_import.sql
+++ b/src/pl/plpython/sql/plpython_import.sql
@@ -7,7 +7,7 @@ CREATE FUNCTION import_fail() returns text
 except ImportError:
 	return "failed as expected"
 return "succeeded, that wasn''t supposed to happen"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 
 CREATE FUNCTION import_succeed() returns text
@@ -28,7 +28,7 @@ except Exception as ex:
 	plpy.notice("import failed -- %s" % str(ex))
 	return "failed, that wasn''t supposed to happen"
 return "succeeded, as expected"'
-    LANGUAGE plpythonu;
+    LANGUAGE plpython3u;
 
 CREATE FUNCTION import_test_one(p text) RETURNS text
 	AS
@@ -39,7 +39,7 @@ except ImportError:
     import sha
     digest = sha.new(p)
 return digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION import_test_two(u users) RETURNS text
 	AS
@@ -51,7 +51,7 @@ except ImportError:
     import sha
     digest = sha.new(plain);
 return "sha hash of " + plain + " is " + digest.hexdigest()'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 -- import python modules
diff --git a/src/pl/plpython/sql/plpython_newline.sql b/src/pl/plpython/sql/plpython_newline.sql
index f9cee9491bb..cb22ba923f9 100644
--- a/src/pl/plpython/sql/plpython_newline.sql
+++ b/src/pl/plpython/sql/plpython_newline.sql
@@ -4,15 +4,15 @@
 
 CREATE OR REPLACE FUNCTION newline_lf() RETURNS integer AS
 E'x = 100\ny = 23\nreturn x + y\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION newline_cr() RETURNS integer AS
 E'x = 100\ry = 23\rreturn x + y\r'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 CREATE OR REPLACE FUNCTION newline_crlf() RETURNS integer AS
 E'x = 100\r\ny = 23\r\nreturn x + y\r\n'
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 
 SELECT newline_lf();
diff --git a/src/pl/plpython/sql/plpython_params.sql b/src/pl/plpython/sql/plpython_params.sql
index ee75c4dc410..8bab4888592 100644
--- a/src/pl/plpython/sql/plpython_params.sql
+++ b/src/pl/plpython/sql/plpython_params.sql
@@ -4,13 +4,13 @@
 
 CREATE FUNCTION test_param_names0(integer, integer) RETURNS int AS $$
 return args[0] + args[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_param_names1(a0 integer, a1 text) RETURNS boolean AS $$
 assert a0 == args[0]
 assert a1 == args[1]
 return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_param_names2(u users) RETURNS text AS $$
 assert u == args[0]
@@ -22,7 +22,7 @@ if isinstance(u, dict):
 else:
     s = str(u)
 return s
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- use deliberately wrong parameter names
 CREATE FUNCTION test_param_names3(a0 integer) RETURNS boolean AS $$
@@ -32,7 +32,7 @@ try:
 except NameError as e:
 	assert e.args[0].find("a1") > -1
 	return True
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 SELECT test_param_names0(2,7);
diff --git a/src/pl/plpython/sql/plpython_quote.sql b/src/pl/plpython/sql/plpython_quote.sql
index 346b5485daf..a1133e7e266 100644
--- a/src/pl/plpython/sql/plpython_quote.sql
+++ b/src/pl/plpython/sql/plpython_quote.sql
@@ -9,7 +9,7 @@ CREATE FUNCTION quote(t text, how text) RETURNS text AS $$
         return plpy.quote_ident(t)
     else:
         raise plpy.Error("unrecognized quote type %s" % how)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT quote(t, 'literal') FROM (VALUES
        ('abc'),
diff --git a/src/pl/plpython/sql/plpython_record.sql b/src/pl/plpython/sql/plpython_record.sql
index 9bab4c9e82d..52bad8bccea 100644
--- a/src/pl/plpython/sql/plpython_record.sql
+++ b/src/pl/plpython/sql/plpython_record.sql
@@ -27,7 +27,7 @@ elif typ == 'obj':
 	type_record.first = first
 	type_record.second = second
 	return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
 if retnull:
@@ -45,20 +45,20 @@ elif typ == 'obj':
 	return type_record
 elif typ == 'str':
 	return "('%s',%r)" % (first, second)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
 return first + '_in_to_out';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 -- Test tuple returning functions
@@ -136,14 +136,14 @@ SELECT * FROM test_type_record_as('obj', 'one', 1, false);
 
 CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
     return { 'first': 'first' }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error1();
 
 
 CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
     return [ 'first' ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error2();
 
@@ -152,12 +152,12 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
     class type_record: pass
     type_record.first = 'first'
     return type_record
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error3();
 
 CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
     return 'foo'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_record_error4();
diff --git a/src/pl/plpython/sql/plpython_setof.sql b/src/pl/plpython/sql/plpython_setof.sql
index 16c2eef0ad6..4cfb10192c0 100644
--- a/src/pl/plpython/sql/plpython_setof.sql
+++ b/src/pl/plpython/sql/plpython_setof.sql
@@ -4,21 +4,21 @@
 
 CREATE FUNCTION test_setof_error() RETURNS SETOF text AS $$
 return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_setof_error();
 
 
 CREATE FUNCTION test_setof_as_list(count integer, content text) RETURNS SETOF text AS $$
 return [ content ]*count
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_as_tuple(count integer, content text) RETURNS SETOF text AS $$
 t = ()
 for i in range(count):
 	t += ( content, )
 return t
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_as_iterator(count integer, content text) RETURNS SETOF text AS $$
 class producer:
@@ -27,13 +27,13 @@ class producer:
 		self.icount = icount
 	def __iter__ (self):
 		return self
-	def next (self):
+	def __next__ (self):
 		if self.icount == 0:
 			raise StopIteration
 		self.icount -= 1
 		return self.icontent
 return producer(count, content)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_setof_spi_in_iterator() RETURNS SETOF text AS
 $$
@@ -42,7 +42,7 @@ $$
         yield s
         plpy.execute('select 2')
 $$
-LANGUAGE plpythonu;
+LANGUAGE plpython3u;
 
 
 -- Test set returning functions
@@ -69,7 +69,7 @@ global x
 while x <= lim:
     yield x
     x = x + 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT ugly(1, 5);
 
@@ -81,7 +81,7 @@ CREATE OR REPLACE FUNCTION get_user_records()
 RETURNS SETOF users
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT get_user_records();
 SELECT * FROM get_user_records();
@@ -91,7 +91,7 @@ CREATE OR REPLACE FUNCTION get_user_records2()
 RETURNS TABLE(fname text, lname text, username text, userid int)
 AS $$
     return plpy.execute("SELECT * FROM users ORDER BY username")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT get_user_records2();
 SELECT * FROM get_user_records2();
diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql
index dd77833ed56..fcd113acaa3 100644
--- a/src/pl/plpython/sql/plpython_spi.sql
+++ b/src/pl/plpython/sql/plpython_spi.sql
@@ -7,19 +7,19 @@ CREATE FUNCTION nested_call_one(a text) RETURNS text
 'q = "SELECT nested_call_two(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 CREATE FUNCTION nested_call_two(a text) RETURNS text
 	AS
 'q = "SELECT nested_call_three(''%s'')" % a
 r = plpy.execute(q)
 return r[0]'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 CREATE FUNCTION nested_call_three(a text) RETURNS text
 	AS
 'return a'
-	LANGUAGE plpythonu ;
+	LANGUAGE plpython3u ;
 
 -- some spi stuff
 
@@ -35,7 +35,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
 	AS
@@ -49,7 +49,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
 	AS
@@ -64,7 +64,7 @@ except Exception as ex:
 	plpy.error(str(ex))
 return None
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION join_sequences(s sequences) RETURNS text
 	AS
@@ -77,7 +77,7 @@ for r in rv:
 	seq = seq + r["sequence"]
 return seq
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 CREATE FUNCTION spi_recursive_sum(a int) RETURNS int
 	AS
@@ -86,7 +86,7 @@ if a > 1:
     r = plpy.execute("SELECT spi_recursive_sum(%d) as a" % (a-1))[0]["a"]
 return a + r
 '
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 --
 -- spi and nested calls
@@ -120,7 +120,7 @@ if result.status() > 0:
    return result.nrows()
 else:
    return None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_metadata_test($$SELECT 1 AS foo, '11'::text AS bar UNION SELECT 2, '22'$$);
 SELECT result_metadata_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
@@ -129,7 +129,7 @@ CREATE FUNCTION result_nrows_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return result.nrows()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_nrows_test($$SELECT 1$$);
 SELECT result_nrows_test($$CREATE TEMPORARY TABLE foo2 (a int, b text)$$);
@@ -140,7 +140,7 @@ CREATE FUNCTION result_len_test(cmd text) RETURNS int
 AS $$
 result = plpy.execute(cmd)
 return len(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_len_test($$SELECT 1$$);
 SELECT result_len_test($$CREATE TEMPORARY TABLE foo3 (a int, b text)$$);
@@ -162,7 +162,7 @@ result[-1] = {'c': 1000}
 result[:2] = [{'c': 10}, {'c': 100}]
 plpy.info([item['c'] for item in result[:]])
 
-# raises TypeError, but the message differs on Python 2.6, so silence it
+# raises TypeError, catch so further tests could be added
 try:
     plpy.info(result['foo'])
 except TypeError:
@@ -170,7 +170,7 @@ except TypeError:
 else:
     assert False, "TypeError not raised"
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_subscript_test();
 
@@ -180,7 +180,7 @@ result = plpy.execute("select 1 where false")
 
 plpy.info(result[:])
 
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_empty_test();
 
@@ -189,7 +189,7 @@ AS $$
 plan = plpy.prepare(cmd)
 result = plpy.execute(plan)
 return str(result)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT result_str_test($$SELECT 1 AS foo UNION SELECT 2$$);
 SELECT result_str_test($$CREATE TEMPORARY TABLE foo1 (a int, b text)$$);
@@ -203,13 +203,13 @@ for row in res:
     if row['lname'] == 'doe':
         does += 1
 return does
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION double_cursor_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
 res.close()
 res.close()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -228,7 +228,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_mix_next_and_fetch() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users order by fname")
@@ -242,7 +242,7 @@ except AttributeError:
 assert item['fname'] == 'rick'
 
 assert len(res.fetch(2)) == 1
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION fetch_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -253,7 +253,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION next_after_close() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users")
@@ -267,7 +267,7 @@ except ValueError:
     pass
 else:
     assert False, "ValueError not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_fetch_next_empty() RETURNS int AS $$
 res = plpy.cursor("select fname, lname from users where false")
@@ -281,7 +281,7 @@ except StopIteration:
     pass
 else:
     assert False, "StopIteration not raised"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan() RETURNS SETOF text AS $$
 plan = plpy.prepare(
@@ -291,13 +291,13 @@ for row in plpy.cursor(plan, ["w"]):
     yield row['fname']
 for row in plan.cursor(["j"]):
     yield row['fname']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
 plan = plpy.prepare("select fname, lname from users where fname like $1 || '%'",
                     ["text"])
 c = plpy.cursor(plan, ["a", "b"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TYPE test_composite_type AS (
   a1 int,
@@ -308,7 +308,7 @@ CREATE OR REPLACE FUNCTION plan_composite_args() RETURNS test_composite_type AS
 plan = plpy.prepare("select $1 as c1", ["test_composite_type"])
 res = plpy.execute(plan, [{"a1": 3, "a2": "label"}])
 return res[0]["c1"]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT simple_cursor_test();
 SELECT double_cursor_close();
diff --git a/src/pl/plpython/sql/plpython_subtransaction.sql b/src/pl/plpython/sql/plpython_subtransaction.sql
index cc4b1ae102b..c65c380f40c 100644
--- a/src/pl/plpython/sql/plpython_subtransaction.sql
+++ b/src/pl/plpython/sql/plpython_subtransaction.sql
@@ -17,7 +17,7 @@ with plpy.subtransaction():
         plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
     elif what_error == "Python":
         raise Exception("Python exception")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_ctx_test();
 SELECT * FROM subtransaction_tbl;
@@ -45,7 +45,7 @@ with plpy.subtransaction():
             raise
         plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_nested_test();
 SELECT * FROM subtransaction_tbl;
@@ -65,7 +65,7 @@ with plpy.subtransaction():
     plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
     plpy.execute("SELECT subtransaction_nested_test('t')")
 return "ok"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_deeply_nested_test();
 SELECT * FROM subtransaction_tbl;
@@ -76,25 +76,25 @@ TRUNCATE subtransaction_tbl;
 CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
 AS $$
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__exit__(None, None, None)
 plpy.subtransaction().__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_twice() RETURNS void
 AS $$
 plpy.subtransaction().__enter__()
 plpy.subtransaction().__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
 AS $$
@@ -102,7 +102,7 @@ s = plpy.subtransaction()
 s.__enter__()
 s.__exit__(None, None, None)
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
 AS $$
@@ -110,14 +110,14 @@ s = plpy.subtransaction()
 s.__enter__()
 s.__enter__()
 s.__exit__(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- No warnings here, as the subtransaction gets indeed closed
 CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
 AS $$
 with plpy.subtransaction() as s:
     s.__enter__()
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
 AS $$
@@ -126,7 +126,7 @@ try:
         s.__exit__(None, None, None)
 except ValueError as e:
     raise ValueError(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_exit_without_enter();
 SELECT subtransaction_enter_without_exit();
@@ -159,7 +159,7 @@ try:
     plpy.execute(p, ["wrong"])
 except plpy.SPIError:
     plpy.warning("Caught a SPI error")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_mix_explicit_and_implicit();
 SELECT * FROM subtransaction_tbl;
@@ -172,7 +172,7 @@ AS $$
 s = plpy.subtransaction()
 s.enter()
 s.exit(None, None, None)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT subtransaction_alternative_names();
 
@@ -186,7 +186,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT try_catch_inside_subtransaction();
 SELECT * FROM subtransaction_tbl;
@@ -202,7 +202,7 @@ with plpy.subtransaction():
          plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
      except plpy.SPIError:
          plpy.notice("caught")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT pk_violation_inside_subtransaction();
 SELECT * FROM subtransaction_tbl;
@@ -217,7 +217,7 @@ with plpy.subtransaction():
     cur.fetch(10)
 fetched = cur.fetch(10);
 return int(fetched[5]["i"])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
 try:
@@ -229,7 +229,7 @@ except plpy.SPIError:
     fetched = cur.fetch(10)
     return int(fetched[5]["i"])
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
 try:
@@ -243,7 +243,7 @@ except plpy.SPIError:
     fetched = cur.fetch(5)
     return fetched[2]["i"]
 return 0 # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
 try:
@@ -254,7 +254,7 @@ except plpy.SPIError:
     cur.close()
     return True
 return False # not reached
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT cursor_in_subxact();
 SELECT cursor_aborted_subxact();
diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql
index 5f1be9c94a8..aa22a274155 100644
--- a/src/pl/plpython/sql/plpython_test.sql
+++ b/src/pl/plpython/sql/plpython_test.sql
@@ -1,13 +1,13 @@
 -- first some tests of basic functionality
-CREATE EXTENSION plpython2u;
+CREATE EXTENSION plpython3u;
 
 -- really stupid function just to get the module loaded
-CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
+CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 
 select stupid();
 
 -- check 2/3 versioning
-CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
+CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython3u;
 
 select stupidn();
 
@@ -21,7 +21,7 @@ for key in keys:
     out.append("%s: %s" % (key, u[key]))
 words = a1 + " " + a2 + " => {" + ", ".join(out) + "}"
 return words'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 select "Argument test #1"(users, fname, lname) from users where lname = 'doe' order by 1;
 
@@ -32,7 +32,7 @@ $$
 contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
 contents.sort()
 return contents
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select module_contents();
 
@@ -47,6 +47,6 @@ plpy.info('info', 37, [1, 2, 3])
 plpy.notice('notice')
 plpy.warning('warning')
 plpy.error('error')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT elog_test_basic();
diff --git a/src/pl/plpython/sql/plpython_transaction.sql b/src/pl/plpython/sql/plpython_transaction.sql
index 68588d9fb02..c939ba76d48 100644
--- a/src/pl/plpython/sql/plpython_transaction.sql
+++ b/src/pl/plpython/sql/plpython_transaction.sql
@@ -2,7 +2,7 @@ CREATE TABLE test1 (a int, b text);
 
 
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -20,7 +20,7 @@ SELECT * FROM test1;
 TRUNCATE test1;
 
 DO
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -37,7 +37,7 @@ TRUNCATE test1;
 
 -- not allowed in a function
 CREATE FUNCTION transaction_test2() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -55,7 +55,7 @@ SELECT * FROM test1;
 
 -- also not allowed if procedure is called from a function
 CREATE FUNCTION transaction_test3() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plpy.execute("CALL transaction_test1()")
 return 1
@@ -68,9 +68,9 @@ SELECT * FROM test1;
 
 -- DO block inside function
 CREATE FUNCTION transaction_test4() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
-plpy.execute("DO LANGUAGE plpythonu $x$ plpy.commit() $x$")
+plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$")
 return 1
 $$;
 
@@ -78,7 +78,7 @@ SELECT transaction_test4();
 
 
 -- commit inside subtransaction (prohibited)
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 s = plpy.subtransaction()
 s.enter()
 plpy.commit()
@@ -91,7 +91,7 @@ INSERT INTO test2 VALUES (0), (1), (2), (3), (4);
 
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.commit()
@@ -106,7 +106,7 @@ SELECT * FROM pg_cursors;
 -- error in cursor loop with commit
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x'])
     plpy.commit()
@@ -120,7 +120,7 @@ SELECT * FROM pg_cursors;
 -- rollback inside cursor loop
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     plpy.rollback()
@@ -134,7 +134,7 @@ SELECT * FROM pg_cursors;
 -- first commit then rollback inside cursor loop
 TRUNCATE test1;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"):
     plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x'])
     if row['x'] % 2 == 0:
@@ -152,7 +152,7 @@ SELECT * FROM pg_cursors;
 CREATE TABLE testpk (id int PRIMARY KEY);
 CREATE TABLE testfk(f1 int REFERENCES testpk DEFERRABLE INITIALLY DEFERRED);
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 # this insert will fail during commit:
 plpy.execute("INSERT INTO testfk VALUES (0)")
 plpy.commit()
@@ -162,7 +162,7 @@ $$;
 SELECT * FROM testpk;
 SELECT * FROM testfk;
 
-DO LANGUAGE plpythonu $$
+DO LANGUAGE plpython3u $$
 # this insert will fail during commit:
 plpy.execute("INSERT INTO testfk VALUES (0)")
 try:
diff --git a/src/pl/plpython/sql/plpython_trigger.sql b/src/pl/plpython/sql/plpython_trigger.sql
index 19852dc5851..e5504b9ab1d 100644
--- a/src/pl/plpython/sql/plpython_trigger.sql
+++ b/src/pl/plpython/sql/plpython_trigger.sql
@@ -16,7 +16,7 @@ if TD["new"]["fname"] == "william":
 	TD["new"]["fname"] = TD["args"][0]
 	rv = "MODIFY"
 return rv'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE FUNCTION users_update() returns trigger
@@ -25,7 +25,7 @@ CREATE FUNCTION users_update() returns trigger
 	if TD["old"]["fname"] != TD["new"]["fname"] and TD["old"]["fname"] == TD["args"][0]:
 		return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE FUNCTION users_delete() RETURNS trigger
@@ -33,7 +33,7 @@ CREATE FUNCTION users_delete() RETURNS trigger
 'if TD["old"]["fname"] == TD["args"][0]:
 	return "SKIP"
 return None'
-	LANGUAGE plpythonu;
+	LANGUAGE plpython3u;
 
 
 CREATE TRIGGER users_insert_trig BEFORE INSERT ON users FOR EACH ROW
@@ -72,7 +72,7 @@ CREATE TABLE trigger_test_generated (
         j int GENERATED ALWAYS AS (i * 2) STORED
 );
 
-CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpython3u AS $$
 
 if 'relid' in TD:
 	TD['relid'] = "bogus:12345"
@@ -157,7 +157,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid1() RETURNS trigger
 AS $$
     return 37
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger1
 BEFORE INSERT ON trigger_test
@@ -173,7 +173,7 @@ DROP TRIGGER stupid_trigger1 ON trigger_test;
 CREATE FUNCTION stupid2() RETURNS trigger
 AS $$
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger2
 BEFORE DELETE ON trigger_test
@@ -191,7 +191,7 @@ INSERT INTO trigger_test VALUES (0, 'zero');
 CREATE FUNCTION stupid3() RETURNS trigger
 AS $$
     return "foo"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
@@ -206,8 +206,8 @@ DROP TRIGGER stupid_trigger3 ON trigger_test;
 
 CREATE FUNCTION stupid3u() RETURNS trigger
 AS $$
-    return u"foo"
-$$ LANGUAGE plpythonu;
+    return "foo"
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger3
 BEFORE UPDATE ON trigger_test
@@ -224,7 +224,7 @@ CREATE FUNCTION stupid4() RETURNS trigger
 AS $$
     del TD["new"]
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger4
 BEFORE UPDATE ON trigger_test
@@ -241,7 +241,7 @@ CREATE FUNCTION stupid5() RETURNS trigger
 AS $$
     TD["new"] = ['foo', 'bar']
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger5
 BEFORE UPDATE ON trigger_test
@@ -258,7 +258,7 @@ CREATE FUNCTION stupid6() RETURNS trigger
 AS $$
     TD["new"] = {1: 'foo', 2: 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger6
 BEFORE UPDATE ON trigger_test
@@ -275,7 +275,7 @@ CREATE FUNCTION stupid7() RETURNS trigger
 AS $$
     TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY";
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
@@ -290,9 +290,9 @@ DROP TRIGGER stupid_trigger7 ON trigger_test;
 
 CREATE FUNCTION stupid7u() RETURNS trigger
 AS $$
-    TD["new"] = {u'v': 'foo', u'a': 'bar'}
+    TD["new"] = {'v': 'foo', 'a': 'bar'}
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER stupid_trigger7
 BEFORE UPDATE ON trigger_test
@@ -318,7 +318,7 @@ CREATE FUNCTION test_null() RETURNS trigger
 AS $$
     TD["new"]['v'] = None
     return "MODIFY"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER test_null_trigger
 BEFORE UPDATE ON trigger_test
@@ -341,7 +341,7 @@ SET DateStyle = 'ISO';
 CREATE FUNCTION set_modif_time() RETURNS trigger AS $$
     TD['new']['modif_time'] = '2010-10-13 21:57:28.930486'
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TABLE pb (a TEXT, modif_time TIMESTAMP(0) WITHOUT TIME ZONE);
 
@@ -365,7 +365,7 @@ CREATE FUNCTION composite_trigger_f() RETURNS trigger AS $$
     TD['new']['f1'] = (3, False)
     TD['new']['f2'] = {'k': 7, 'l': 'yes', 'ignored': 10}
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_f();
@@ -380,7 +380,7 @@ CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
 
 CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
@@ -399,7 +399,7 @@ CREATE TABLE composite_trigger_nested_test(c comp3);
 
 CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
     return 'MODIFY'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
   FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
@@ -410,7 +410,7 @@ INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'
 SELECT * FROM composite_trigger_nested_test;
 
 -- check that using a function as a trigger over two tables works correctly
-CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
+CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpython3u AS $$
     TD["new"]["data"] = '1234'
     return 'MODIFY'
 $$;
@@ -432,7 +432,7 @@ SELECT * FROM b;
 CREATE TABLE transition_table_test (id int, name text);
 INSERT INTO transition_table_test VALUES (1, 'a');
 
-CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpython3u AS
 $$
     rv = plpy.execute("SELECT * FROM old_table")
     assert(rv.nrows() == 1)
@@ -455,7 +455,7 @@ DROP FUNCTION transition_table_test_f();
 -- dealing with generated columns
 
 CREATE FUNCTION generated_test_func1() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 TD['new']['j'] = 5  # not allowed
 return 'MODIFY'
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
index 0d207d9c015..40f4f79d99f 100644
--- a/src/pl/plpython/sql/plpython_types.sql
+++ b/src/pl/plpython/sql/plpython_types.sql
@@ -9,7 +9,7 @@
 CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bool(true);
 SELECT * FROM test_type_conversion_bool(false);
@@ -35,7 +35,7 @@ elif n == 5:
    ret = [0]
 plpy.info(ret, not not ret)
 return ret
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bool_other(0);
 SELECT * FROM test_type_conversion_bool_other(1);
@@ -48,7 +48,7 @@ SELECT * FROM test_type_conversion_bool_other(5);
 CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_char('a');
 SELECT * FROM test_type_conversion_char(null);
@@ -57,7 +57,7 @@ SELECT * FROM test_type_conversion_char(null);
 CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int2(100::int2);
 SELECT * FROM test_type_conversion_int2(-100::int2);
@@ -67,7 +67,7 @@ SELECT * FROM test_type_conversion_int2(null);
 CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int4(100);
 SELECT * FROM test_type_conversion_int4(-100);
@@ -77,7 +77,7 @@ SELECT * FROM test_type_conversion_int4(null);
 CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_int8(100);
 SELECT * FROM test_type_conversion_int8(-100);
@@ -90,7 +90,7 @@ CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
 # between decimal and cdecimal
 plpy.info(str(x), x.__class__.__name__)
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_numeric(100);
 SELECT * FROM test_type_conversion_numeric(-100);
@@ -105,7 +105,7 @@ SELECT * FROM test_type_conversion_numeric(null);
 CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_float4(100);
 SELECT * FROM test_type_conversion_float4(-100);
@@ -116,7 +116,7 @@ SELECT * FROM test_type_conversion_float4(null);
 CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_float8(100);
 SELECT * FROM test_type_conversion_float8(-100);
@@ -128,7 +128,7 @@ SELECT * FROM test_type_conversion_float8(100100100.654321);
 CREATE FUNCTION test_type_conversion_oid(x oid) RETURNS oid AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_oid(100);
 SELECT * FROM test_type_conversion_oid(2147483649);
@@ -138,7 +138,7 @@ SELECT * FROM test_type_conversion_oid(null);
 CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_text('hello world');
 SELECT * FROM test_type_conversion_text(null);
@@ -147,7 +147,7 @@ SELECT * FROM test_type_conversion_text(null);
 CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bytea('hello world');
 SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
@@ -157,7 +157,7 @@ SELECT * FROM test_type_conversion_bytea(null);
 CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
 import marshal
 return marshal.dumps('hello world')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
 import marshal
@@ -165,7 +165,7 @@ try:
     return marshal.loads(x)
 except ValueError as e:
     return 'FAILED: ' + str(e)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
 
@@ -178,7 +178,7 @@ CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
 
 CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_booltrue(true, true);
 SELECT * FROM test_type_conversion_booltrue(false, true);
@@ -190,7 +190,7 @@ CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
 CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
 SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
@@ -201,7 +201,7 @@ CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
 
 CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_nnint(10, 20);
 SELECT * FROM test_type_conversion_nnint(null, 20);
@@ -213,7 +213,7 @@ CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT
 CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
 plpy.info(x, type(x))
 return y
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
 SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
@@ -229,7 +229,7 @@ SELECT * FROM test_type_conversion_bytea10('hello word', null);
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
 SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
@@ -243,14 +243,14 @@ SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
 CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
 
 CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
             [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
@@ -258,7 +258,7 @@ SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',
 CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
             [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
@@ -270,7 +270,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemint4(8,5,3,2);
 
@@ -278,7 +278,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 )
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemint8(5,5,3,2);
 
@@ -286,7 +286,7 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemfloat4(6,5,3,2);
 
@@ -294,14 +294,14 @@ CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4
 m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
 plpy.info(m, type(m))
 return m
-$BODY$ LANGUAGE plpythonu;
+$BODY$ LANGUAGE plpython3u;
 
 select pyreturnmultidemfloat8(7,5,3,2);
 
 CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
 SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
@@ -310,59 +310,59 @@ SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar
 CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
 
 
 CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_mixed1();
 
 
 CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
 return [123, 'abc']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_mixed2();
 
 CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
 return [[1,2,3],[4,5]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_mdarray_malformed();
 
 CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
 return [[[[[[[1]]]]]]]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_mdarray_toodeep();
 
 
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_record();
 
 
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_string();
 
 CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
 return ('abc', 'def')
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_tuple();
 
 CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
 return 5
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_error();
 
@@ -376,14 +376,14 @@ CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AN
 CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain);
 SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain);
 
 CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$
 return [2,1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 SELECT * FROM test_type_conversion_array_domain_check_violation();
 
 
@@ -394,13 +394,13 @@ SELECT * FROM test_type_conversion_array_domain_check_violation();
 CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_read_uint2_array(array[1::uint2]);
 
 CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_build_uint2_array(1::int2);
 select test_build_uint2_array(-1::int2);  -- fail
@@ -413,7 +413,7 @@ select test_build_uint2_array(-1::int2);  -- fail
 CREATE FUNCTION test_type_conversion_domain_array(x integer[])
   RETURNS ordered_pair_domain[] AS $$
 return [x, x]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_domain_array(array[2,4]);
 select test_type_conversion_domain_array(array[4,2]);  -- fail
@@ -422,7 +422,7 @@ CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
   RETURNS integer AS $$
 plpy.info(x, type(x))
 return x[1]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_domain_array2(array[2,4]);
 select test_type_conversion_domain_array2(array[4,2]);  -- fail
@@ -431,7 +431,7 @@ CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
   RETURNS ordered_pair_domain AS $$
 plpy.info(x, type(x))
 return x[0]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
 
@@ -450,7 +450,7 @@ INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10);
 
 CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$
 return e['basesalary'] + e['bonus']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT name, test_composite_table_input(employee.*) FROM employee;
 
@@ -470,7 +470,7 @@ CREATE TYPE named_pair AS (
 
 CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$
 return sum(p.values())
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT test_composite_type_input(row(1, 2));
 
@@ -487,7 +487,7 @@ CREATE TYPE nnint_container AS (f1 int, f2 nnint);
 
 CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
 return {'f1': x, 'f2': y}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT nnint_test(null, 3);
 SELECT nnint_test(3, null);  -- fail
@@ -501,21 +501,21 @@ CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
 
 CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
 return p['i'] + p['j']
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT read_ordered_named_pair(row(1, 2));
 SELECT read_ordered_named_pair(row(2, 1));  -- fail
 
 CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
 return {'i': i, 'j': j}
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT build_ordered_named_pair(1,2);
 SELECT build_ordered_named_pair(2,1);  -- fail
 
 CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
 return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT build_ordered_named_pairs(1,2);
 SELECT build_ordered_named_pairs(2,1);  -- fail
@@ -526,7 +526,7 @@ SELECT build_ordered_named_pairs(2,1);  -- fail
 --
 
 CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean'])
 rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python
@@ -537,7 +537,7 @@ SELECT test_prep_bool_input(); -- 1
 
 
 CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT $1 = 1 AS val", ['int'])
 rv = plpy.execute(plan, [0], 5)
@@ -549,7 +549,7 @@ SELECT test_prep_bool_output(); -- false
 
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea'])
 rv = plpy.execute(plan, [bb], 5)
@@ -560,7 +560,7 @@ SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated
 
 
 CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val")
 rv = plpy.execute(plan, [], 5)
diff --git a/src/pl/plpython/sql/plpython_unicode.sql b/src/pl/plpython/sql/plpython_unicode.sql
index a11e5eeaa21..14f7b4e0053 100644
--- a/src/pl/plpython/sql/plpython_unicode.sql
+++ b/src/pl/plpython/sql/plpython_unicode.sql
@@ -14,28 +14,28 @@ CREATE TABLE unicode_test (
 );
 
 CREATE FUNCTION unicode_return() RETURNS text AS E'
-return u"\\xA0"
-' LANGUAGE plpythonu;
+return "\\xA0"
+' LANGUAGE plpython3u;
 
 CREATE FUNCTION unicode_trigger() RETURNS trigger AS E'
-TD["new"]["testvalue"] = u"\\xA0"
+TD["new"]["testvalue"] = "\\xA0"
 return "MODIFY"
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test
   FOR EACH ROW EXECUTE PROCEDURE unicode_trigger();
 
 CREATE FUNCTION unicode_plan1() RETURNS text AS E'
 plan = plpy.prepare("SELECT $1 AS testvalue", ["text"])
-rv = plpy.execute(plan, [u"\\xA0"], 1)
+rv = plpy.execute(plan, ["\\xA0"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 CREATE FUNCTION unicode_plan2() RETURNS text AS E'
-plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"])
+plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"])
 rv = plpy.execute(plan, ["foo", "bar"], 1)
 return rv[0]["testvalue"]
-' LANGUAGE plpythonu;
+' LANGUAGE plpython3u;
 
 
 SELECT unicode_return();
diff --git a/src/pl/plpython/sql/plpython_void.sql b/src/pl/plpython/sql/plpython_void.sql
index 77d7f59e4c7..5a1a6711fb0 100644
--- a/src/pl/plpython/sql/plpython_void.sql
+++ b/src/pl/plpython/sql/plpython_void.sql
@@ -4,16 +4,16 @@
 
 CREATE FUNCTION test_void_func1() RETURNS void AS $$
 x = 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 -- illegal: can't return non-None value in void-returning func
 CREATE FUNCTION test_void_func2() RETURNS void AS $$
 return 10
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION test_return_none() RETURNS int AS $$
 None
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 
 -- Tests for functions returning void
diff --git a/contrib/hstore_plpython/.gitignore b/contrib/hstore_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/hstore_plpython/.gitignore
+++ b/contrib/hstore_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile
index 19d99a8045e..9d88cda1d0f 100644
--- a/contrib/hstore_plpython/Makefile
+++ b/contrib/hstore_plpython/Makefile
@@ -10,7 +10,6 @@ EXTENSION = hstore_plpython3u
 DATA = hstore_plpython3u--1.0.sql
 
 REGRESS = hstore_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 PG_CPPFLAGS = $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
@@ -37,9 +36,4 @@ SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
 
 REGRESS_OPTS += --load-extension=hstore
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=hstore_plpythonu
-endif
 EXTRA_INSTALL += contrib/hstore
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out
index ecf1dd61bc1..bf238701fec 100644
--- a/contrib/hstore_plpython/expected/hstore_plpython.out
+++ b/contrib/hstore_plpython/expected/hstore_plpython.out
@@ -1,8 +1,8 @@
-CREATE EXTENSION hstore_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION hstore_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 -- test hstore -> python
 CREATE FUNCTION test1(val hstore) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -18,7 +18,7 @@ INFO:  [('aa', 'bb'), ('cc', None)]
 
 -- the same with the versioned language name
 CREATE FUNCTION test1n(val hstore) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -34,7 +34,7 @@ INFO:  [('aa', 'bb'), ('cc', None)]
 
 -- test hstore[] -> python
 CREATE FUNCTION test1arr(val hstore[]) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}])
@@ -48,7 +48,7 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']);
 
 -- test python -> hstore
 CREATE FUNCTION test2(a int, b text) RETURNS hstore
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = {'a': a, 'b': b, 'c': None}
@@ -65,14 +65,14 @@ SELECT test2(1, 'boo');
 CREATE OR REPLACE FUNCTION public.test2(a integer, b text)
  RETURNS hstore
  TRANSFORM FOR TYPE hstore
- LANGUAGE plpythonu
+ LANGUAGE plpython3u
 AS $function$
 val = {'a': a, 'b': b, 'c': None}
 return val
 $function$
 -- test python -> hstore[]
 CREATE FUNCTION test2arr() RETURNS hstore[]
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
@@ -87,7 +87,7 @@ SELECT test2arr();
 -- test python -> domain over hstore
 CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
 CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 return {'a': 1, fn: 'boo', 'c': None}
@@ -104,7 +104,7 @@ CONTEXT:  while creating return value
 PL/Python function "test2dom"
 -- test as part of prepare/execute
 CREATE FUNCTION test3() RETURNS void
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1")
@@ -131,7 +131,7 @@ SELECT * FROM test1;
 (1 row)
 
 CREATE FUNCTION test4() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}})
diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql
index b6d98b7dd53..a9cfbbe13e2 100644
--- a/contrib/hstore_plpython/sql/hstore_plpython.sql
+++ b/contrib/hstore_plpython/sql/hstore_plpython.sql
@@ -1,9 +1,9 @@
-CREATE EXTENSION hstore_plpython2u CASCADE;
+CREATE EXTENSION hstore_plpython3u CASCADE;
 
 
 -- test hstore -> python
 CREATE FUNCTION test1(val hstore) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -16,7 +16,7 @@ SELECT test1('aa=>bb, cc=>NULL'::hstore);
 
 -- the same with the versioned language name
 CREATE FUNCTION test1n(val hstore) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
@@ -29,7 +29,7 @@ SELECT test1n('aa=>bb, cc=>NULL'::hstore);
 
 -- test hstore[] -> python
 CREATE FUNCTION test1arr(val hstore[]) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}])
@@ -41,7 +41,7 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']);
 
 -- test python -> hstore
 CREATE FUNCTION test2(a int, b text) RETURNS hstore
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = {'a': a, 'b': b, 'c': None}
@@ -56,7 +56,7 @@ SELECT test2(1, 'boo');
 
 -- test python -> hstore[]
 CREATE FUNCTION test2arr() RETURNS hstore[]
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
@@ -70,7 +70,7 @@ SELECT test2arr();
 CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
 
 CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 return {'a': 1, fn: 'boo', 'c': None}
@@ -82,7 +82,7 @@ SELECT test2dom('bar');  -- fail
 
 -- test as part of prepare/execute
 CREATE FUNCTION test3() RETURNS void
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1")
@@ -103,7 +103,7 @@ INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL');
 SELECT * FROM test1;
 
 CREATE FUNCTION test4() RETURNS trigger
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}})
diff --git a/contrib/jsonb_plpython/.gitignore b/contrib/jsonb_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/jsonb_plpython/.gitignore
+++ b/contrib/jsonb_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/jsonb_plpython/Makefile b/contrib/jsonb_plpython/Makefile
index eaab5ca2601..fea7bdfc00f 100644
--- a/contrib/jsonb_plpython/Makefile
+++ b/contrib/jsonb_plpython/Makefile
@@ -12,7 +12,6 @@ EXTENSION = jsonb_plpython3u
 DATA = jsonb_plpython3u--1.0.sql
 
 REGRESS = jsonb_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -33,9 +32,3 @@ else
 rpathdir = $(python_libdir)
 SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
-
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=jsonb_plpythonu
-endif
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/jsonb_plpython/expected/jsonb_plpython.out b/contrib/jsonb_plpython/expected/jsonb_plpython.out
index b491fe9cc68..cac963de69c 100644
--- a/contrib/jsonb_plpython/expected/jsonb_plpython.out
+++ b/contrib/jsonb_plpython/expected/jsonb_plpython.out
@@ -1,8 +1,8 @@
-CREATE EXTENSION jsonb_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION jsonb_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 -- test jsonb -> python dict
 CREATE FUNCTION test1(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -18,7 +18,7 @@ SELECT test1('{"a": 1, "c": "NULL"}'::jsonb);
 -- test jsonb -> python dict
 -- complex dict with dicts as value
 CREATE FUNCTION test1complex(val jsonb) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -34,7 +34,7 @@ SELECT test1complex('{"d": {"d": 1}}'::jsonb);
 -- test jsonb[] -> python dict
 -- dict with array as value
 CREATE FUNCTION test1arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -50,7 +50,7 @@ SELECT test1arr('{"d":[12, 1]}'::jsonb);
 -- test jsonb[] -> python list
 -- simple list
 CREATE FUNCTION test2arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -66,7 +66,7 @@ SELECT test2arr('[12, 1]'::jsonb);
 -- test jsonb[] -> python list
 -- array of dicts
 CREATE FUNCTION test3arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -81,7 +81,7 @@ SELECT test3arr('[{"a": 1, "b": 2}, {"c": 3,"d": 4}]'::jsonb);
 
 -- test jsonb int -> python int
 CREATE FUNCTION test1int(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == 1)
@@ -95,7 +95,7 @@ SELECT test1int('1'::jsonb);
 
 -- test jsonb string -> python string
 CREATE FUNCTION test1string(val jsonb) RETURNS text
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == "a")
@@ -109,7 +109,7 @@ SELECT test1string('"a"'::jsonb);
 
 -- test jsonb null -> python None
 CREATE FUNCTION test1null(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == None)
@@ -123,7 +123,7 @@ SELECT test1null('null'::jsonb);
 
 -- test python -> jsonb
 CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 as $$
 return val
@@ -238,7 +238,7 @@ SELECT roundtrip('["string", "string2"]'::jsonb);
 
 -- complex numbers -> jsonb
 CREATE FUNCTION testComplexNumbers() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 1 + 2j
@@ -250,7 +250,7 @@ CONTEXT:  while creating return value
 PL/Python function "testcomplexnumbers"
 -- range -> jsonb
 CREATE FUNCTION testRange() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = range(3)
@@ -264,7 +264,7 @@ SELECT testRange();
 
 -- 0xff -> jsonb
 CREATE FUNCTION testDecimal() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 0xff
@@ -278,7 +278,7 @@ SELECT testDecimal();
 
 -- tuple -> jsonb
 CREATE FUNCTION testTuple() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = (1, 'String', None)
@@ -292,7 +292,7 @@ SELECT testTuple();
 
 -- interesting dict -> jsonb
 CREATE FUNCTION test_dict1() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = {"a": 1, None: 2, 33: 3}
diff --git a/contrib/jsonb_plpython/sql/jsonb_plpython.sql b/contrib/jsonb_plpython/sql/jsonb_plpython.sql
index 2ee1bca0a98..29dc33279a0 100644
--- a/contrib/jsonb_plpython/sql/jsonb_plpython.sql
+++ b/contrib/jsonb_plpython/sql/jsonb_plpython.sql
@@ -1,8 +1,8 @@
-CREATE EXTENSION jsonb_plpython2u CASCADE;
+CREATE EXTENSION jsonb_plpython3u CASCADE;
 
 -- test jsonb -> python dict
 CREATE FUNCTION test1(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -15,7 +15,7 @@ SELECT test1('{"a": 1, "c": "NULL"}'::jsonb);
 -- test jsonb -> python dict
 -- complex dict with dicts as value
 CREATE FUNCTION test1complex(val jsonb) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -29,7 +29,7 @@ SELECT test1complex('{"d": {"d": 1}}'::jsonb);
 -- test jsonb[] -> python dict
 -- dict with array as value
 CREATE FUNCTION test1arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, dict)
@@ -42,7 +42,7 @@ SELECT test1arr('{"d":[12, 1]}'::jsonb);
 -- test jsonb[] -> python list
 -- simple list
 CREATE FUNCTION test2arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -55,7 +55,7 @@ SELECT test2arr('[12, 1]'::jsonb);
 -- test jsonb[] -> python list
 -- array of dicts
 CREATE FUNCTION test3arr(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert isinstance(val, list)
@@ -67,7 +67,7 @@ SELECT test3arr('[{"a": 1, "b": 2}, {"c": 3,"d": 4}]'::jsonb);
 
 -- test jsonb int -> python int
 CREATE FUNCTION test1int(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == 1)
@@ -78,7 +78,7 @@ SELECT test1int('1'::jsonb);
 
 -- test jsonb string -> python string
 CREATE FUNCTION test1string(val jsonb) RETURNS text
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == "a")
@@ -89,7 +89,7 @@ SELECT test1string('"a"'::jsonb);
 
 -- test jsonb null -> python None
 CREATE FUNCTION test1null(val jsonb) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 assert(val == None)
@@ -100,7 +100,7 @@ SELECT test1null('null'::jsonb);
 
 -- test python -> jsonb
 CREATE FUNCTION roundtrip(val jsonb) RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 as $$
 return val
@@ -129,7 +129,7 @@ SELECT roundtrip('["string", "string2"]'::jsonb);
 
 -- complex numbers -> jsonb
 CREATE FUNCTION testComplexNumbers() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 1 + 2j
@@ -140,7 +140,7 @@ SELECT testComplexNumbers();
 
 -- range -> jsonb
 CREATE FUNCTION testRange() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = range(3)
@@ -151,7 +151,7 @@ SELECT testRange();
 
 -- 0xff -> jsonb
 CREATE FUNCTION testDecimal() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = 0xff
@@ -162,7 +162,7 @@ SELECT testDecimal();
 
 -- tuple -> jsonb
 CREATE FUNCTION testTuple() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = (1, 'String', None)
@@ -173,7 +173,7 @@ SELECT testTuple();
 
 -- interesting dict -> jsonb
 CREATE FUNCTION test_dict1() RETURNS jsonb
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE jsonb
 AS $$
 x = {"a": 1, None: 2, 33: 3}
diff --git a/contrib/ltree_plpython/.gitignore b/contrib/ltree_plpython/.gitignore
index ce6fab94a0f..5dcb3ff9723 100644
--- a/contrib/ltree_plpython/.gitignore
+++ b/contrib/ltree_plpython/.gitignore
@@ -1,6 +1,4 @@
 # Generated subdirectories
-/expected/python3/
 /log/
 /results/
-/sql/python3/
 /tmp_check/
diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile
index 0bccb111e6b..406d2789c9c 100644
--- a/contrib/ltree_plpython/Makefile
+++ b/contrib/ltree_plpython/Makefile
@@ -10,7 +10,6 @@ EXTENSION = ltree_plpython3u
 DATA = ltree_plpython3u--1.0.sql
 
 REGRESS = ltree_plpython
-REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
 
 PG_CPPFLAGS = $(python_includespec) -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"'
 
@@ -37,9 +36,4 @@ SHLIB_LINK += $(python_libspec) $(python_additional_libs)
 endif
 
 REGRESS_OPTS += --load-extension=ltree
-ifeq ($(python_majorversion),2)
-REGRESS_OPTS += --load-extension=plpythonu --load-extension=ltree_plpythonu
-endif
 EXTRA_INSTALL += contrib/ltree
-
-include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk
diff --git a/contrib/ltree_plpython/expected/ltree_plpython.out b/contrib/ltree_plpython/expected/ltree_plpython.out
index f28897fee48..bd32541fdb3 100644
--- a/contrib/ltree_plpython/expected/ltree_plpython.out
+++ b/contrib/ltree_plpython/expected/ltree_plpython.out
@@ -1,7 +1,7 @@
-CREATE EXTENSION ltree_plpython2u CASCADE;
-NOTICE:  installing required extension "plpython2u"
+CREATE EXTENSION ltree_plpython3u CASCADE;
+NOTICE:  installing required extension "plpython3u"
 CREATE FUNCTION test1(val ltree) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -15,7 +15,7 @@ INFO:  ['aa', 'bb', 'cc']
 (1 row)
 
 CREATE FUNCTION test1n(val ltree) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -29,7 +29,7 @@ INFO:  ['aa', 'bb', 'cc']
 (1 row)
 
 CREATE FUNCTION test2() RETURNS ltree
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 return ['foo', 'bar', 'baz']
diff --git a/contrib/ltree_plpython/sql/ltree_plpython.sql b/contrib/ltree_plpython/sql/ltree_plpython.sql
index 210f5428a5a..0b8d28399a6 100644
--- a/contrib/ltree_plpython/sql/ltree_plpython.sql
+++ b/contrib/ltree_plpython/sql/ltree_plpython.sql
@@ -1,8 +1,8 @@
-CREATE EXTENSION ltree_plpython2u CASCADE;
+CREATE EXTENSION ltree_plpython3u CASCADE;
 
 
 CREATE FUNCTION test1(val ltree) RETURNS int
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -13,7 +13,7 @@ SELECT test1('aa.bb.cc'::ltree);
 
 
 CREATE FUNCTION test1n(val ltree) RETURNS int
-LANGUAGE plpython2u
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 plpy.info(repr(val))
@@ -24,7 +24,7 @@ SELECT test1n('aa.bb.cc'::ltree);
 
 
 CREATE FUNCTION test2() RETURNS ltree
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 TRANSFORM FOR TYPE ltree
 AS $$
 return ['foo', 'bar', 'baz']
diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl
index e2b0db0879b..625f6fb88ae 100644
--- a/src/tools/msvc/vcregress.pl
+++ b/src/tools/msvc/vcregress.pl
@@ -314,55 +314,6 @@ sub taptest
 	return;
 }
 
-sub mangle_plpython3
-{
-	my $tests = shift;
-	mkdir "results" unless -d "results";
-	mkdir "sql/python3";
-	mkdir "results/python3";
-	mkdir "expected/python3";
-
-	foreach my $test (@$tests)
-	{
-		local $/ = undef;
-		foreach my $dir ('sql', 'expected')
-		{
-			my $extension = ($dir eq 'sql' ? 'sql' : 'out');
-
-			my @files =
-			  glob("$dir/$test.$extension $dir/${test}_[0-9].$extension");
-			foreach my $file (@files)
-			{
-				open(my $handle, '<', $file)
-				  || die "test file $file not found";
-				my $contents = <$handle>;
-				close($handle);
-				do
-				{
-					s/<type 'exceptions\.([[:alpha:]]*)'>/<class '$1'>/g;
-					s/<type 'long'>/<class 'int'>/g;
-					s/([0-9][0-9]*)L/$1/g;
-					s/([ [{])u"/$1"/g;
-					s/([ [{])u'/$1'/g;
-					s/def next/def __next__/g;
-					s/LANGUAGE plpython2?u/LANGUAGE plpython3u/g;
-					s/EXTENSION (\S*?)plpython2?u/EXTENSION $1plpython3u/g;
-					s/installing required extension "plpython2u"/installing required extension "plpython3u"/g;
-				  }
-				  for ($contents);
-				my $base = basename $file;
-				open($handle, '>', "$dir/python3/$base")
-				  || die "opening python 3 file for $file";
-				print $handle $contents;
-				close($handle);
-			}
-		}
-	}
-	do { s!^!python3/!; }
-	  foreach (@$tests);
-	return @$tests;
-}
-
 sub plcheck
 {
 	chdir "$topdir/src/pl";
@@ -386,8 +337,7 @@ sub plcheck
 		if ($lang eq 'plpython')
 		{
 			next
-			  unless -d "$topdir/$Config/plpython2"
-			  || -d "$topdir/$Config/plpython3";
+			  unless -d "$topdir/$Config/plpython3";
 			$lang = 'plpythonu';
 		}
 		else
@@ -397,8 +347,6 @@ sub plcheck
 		my @lang_args = ("--load-extension=$lang");
 		chdir $dir;
 		my @tests = fetchTests();
-		@tests = mangle_plpython3(\@tests)
-		  if $lang eq 'plpythonu' && -d "$topdir/$Config/plpython3";
 		if ($lang eq 'plperl')
 		{
 
@@ -462,28 +410,6 @@ sub subdircheck
 
 	my @opts = fetchRegressOpts();
 
-	# Special processing for python transform modules, see their respective
-	# Makefiles for more details regarding Python-version specific
-	# dependencies.
-	if ($module =~ /_plpython$/)
-	{
-		die "Python not enabled in configuration"
-		  if !defined($config->{python});
-
-		@opts = grep { $_ !~ /plpythonu/ } @opts;
-
-		if (-d "$topdir/$Config/plpython2")
-		{
-			push @opts, "--load-extension=plpythonu";
-			push @opts, '--load-extension=' . $module . 'u';
-		}
-		else
-		{
-			# must be python 3
-			@tests = mangle_plpython3(\@tests);
-		}
-	}
-
 	print "============================================================\n";
 	print "Checking $module\n";
 	my @args = (
-- 
2.35.1.354.g715d08a9e5

v7-0003-plpython-Code-cleanup-related-to-removal-of-Pytho.patchtext/x-diff; charset=us-asciiDownload
From 0227fb1a996315c61bda26aa905d5b1ed30da8bd Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 6 Mar 2022 16:57:28 -0800
Subject: [PATCH v7 3/4] plpython: Code cleanup related to removal of Python 2
 support.

Reviewed-By: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 src/pl/plpython/plpy_cursorobject.c       |  8 +--
 src/pl/plpython/plpy_elog.c               | 26 +++++-----
 src/pl/plpython/plpy_exec.c               | 44 ++++++++---------
 src/pl/plpython/plpy_main.c               | 59 ++++-------------------
 src/pl/plpython/plpy_planobject.c         |  2 +-
 src/pl/plpython/plpy_plpymodule.c         | 32 +++---------
 src/pl/plpython/plpy_plpymodule.h         |  2 -
 src/pl/plpython/plpy_resultobject.c       | 16 ++----
 src/pl/plpython/plpy_spi.c                | 10 ++--
 src/pl/plpython/plpy_typeio.c             | 28 ++++-------
 src/pl/plpython/plpy_util.c               |  9 ----
 src/pl/plpython/plpy_util.h               |  2 -
 src/pl/plpython/plpython.h                | 34 +------------
 contrib/hstore_plpython/hstore_plpython.c | 12 ++---
 contrib/jsonb_plpython/jsonb_plpython.c   | 11 ++---
 contrib/ltree_plpython/ltree_plpython.c   |  6 +--
 16 files changed, 81 insertions(+), 220 deletions(-)

diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index 08d8b607e38..6b6e7433453 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -40,7 +40,7 @@ static PyTypeObject PLy_CursorType = {
 	.tp_name = "PLyCursor",
 	.tp_basicsize = sizeof(PLyCursorObject),
 	.tp_dealloc = PLy_cursor_dealloc,
-	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER,
+	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
 	.tp_doc = PLy_cursor_doc,
 	.tp_iter = PyObject_SelfIter,
 	.tp_iternext = PLy_cursor_iternext,
@@ -150,7 +150,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
 
 	if (args)
 	{
-		if (!PySequence_Check(args) || PyString_Check(args) || PyUnicode_Check(args))
+		if (!PySequence_Check(args) || PyUnicode_Check(args))
 		{
 			PLy_exception_set(PyExc_TypeError, "plpy.cursor takes a sequence as its second argument");
 			return NULL;
@@ -169,7 +169,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
 
 		if (!so)
 			PLy_elog(ERROR, "could not execute plan");
-		sv = PyString_AsString(so);
+		sv = PLyUnicode_AsString(so);
 		PLy_exception_set_plural(PyExc_TypeError,
 								 "Expected sequence of %d argument, got %d: %s",
 								 "Expected sequence of %d arguments, got %d: %s",
@@ -410,7 +410,7 @@ PLy_cursor_fetch(PyObject *self, PyObject *args)
 		SPI_cursor_fetch(portal, true, count);
 
 		Py_DECREF(ret->status);
-		ret->status = PyInt_FromLong(SPI_OK_FETCH);
+		ret->status = PyLong_FromLong(SPI_OK_FETCH);
 
 		Py_DECREF(ret->nrows);
 		ret->nrows = PyLong_FromUnsignedLongLong(SPI_processed);
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index 224b8836fba..7c627eacfbf 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -193,24 +193,20 @@ PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
 	e_type_o = PyObject_GetAttrString(e, "__name__");
 	e_module_o = PyObject_GetAttrString(e, "__module__");
 	if (e_type_o)
-		e_type_s = PyString_AsString(e_type_o);
+		e_type_s = PLyUnicode_AsString(e_type_o);
 	if (e_type_s)
-		e_module_s = PyString_AsString(e_module_o);
+		e_module_s = PLyUnicode_AsString(e_module_o);
 
 	if (v && ((vob = PyObject_Str(v)) != NULL))
-		vstr = PyString_AsString(vob);
+		vstr = PLyUnicode_AsString(vob);
 	else
 		vstr = "unknown";
 
 	initStringInfo(&xstr);
 	if (!e_type_s || !e_module_s)
 	{
-		if (PyString_Check(e))
-			/* deprecated string exceptions */
-			appendStringInfoString(&xstr, PyString_AsString(e));
-		else
-			/* shouldn't happen */
-			appendStringInfoString(&xstr, "unrecognized exception");
+		/* shouldn't happen */
+		appendStringInfoString(&xstr, "unrecognized exception");
 	}
 	/* mimics behavior of traceback.format_exception_only */
 	else if (strcmp(e_module_s, "builtins") == 0
@@ -290,11 +286,11 @@ PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
 			if (*tb_depth == 1)
 				fname = "<module>";
 			else
-				fname = PyString_AsString(name);
+				fname = PLyUnicode_AsString(name);
 
 			proname = PLy_procedure_name(exec_ctx->curr_proc);
-			plain_filename = PyString_AsString(filename);
-			plain_lineno = PyInt_AsLong(lineno);
+			plain_filename = PLyUnicode_AsString(filename);
+			plain_lineno = PyLong_AsLong(lineno);
 
 			if (proname == NULL)
 				appendStringInfo(&tbstr, "\n  PL/Python anonymous code block, line %ld, in %s",
@@ -365,7 +361,7 @@ PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
 	if (sqlstate == NULL)
 		return;
 
-	buffer = PyString_AsString(sqlstate);
+	buffer = PLyUnicode_AsString(sqlstate);
 	if (strlen(buffer) == 5 &&
 		strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
 	{
@@ -573,7 +569,7 @@ get_string_attr(PyObject *obj, char *attrname, char **str)
 	val = PyObject_GetAttrString(obj, attrname);
 	if (val != NULL && val != Py_None)
 	{
-		*str = pstrdup(PyString_AsString(val));
+		*str = pstrdup(PLyUnicode_AsString(val));
 	}
 	Py_XDECREF(val);
 }
@@ -589,7 +585,7 @@ set_string_attr(PyObject *obj, char *attrname, char *str)
 
 	if (str != NULL)
 	{
-		val = PyString_FromString(str);
+		val = PLyUnicode_FromString(str);
 		if (!val)
 			return false;
 	}
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index c6f6a6fbcca..150b3a5977f 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -294,7 +294,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
 /* trigger subhandler
  *
  * the python function is expected to return Py_None if the tuple is
- * acceptable and unmodified.  Otherwise it should return a PyString
+ * acceptable and unmodified.  Otherwise it should return a PyUnicode
  * object who's value is SKIP, or MODIFY.  SKIP means don't perform
  * this action.  MODIFY means the tuple has been modified, so update
  * tuple and perform action.  SKIP and MODIFY assume the trigger fires
@@ -360,9 +360,7 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
 		{
 			char	   *srv;
 
-			if (PyString_Check(plrv))
-				srv = PyString_AsString(plrv);
-			else if (PyUnicode_Check(plrv))
+			if (PyUnicode_Check(plrv))
 				srv = PLyUnicode_AsString(plrv);
 			else
 			{
@@ -700,35 +698,35 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 		if (!pltdata)
 			return NULL;
 
-		pltname = PyString_FromString(tdata->tg_trigger->tgname);
+		pltname = PLyUnicode_FromString(tdata->tg_trigger->tgname);
 		PyDict_SetItemString(pltdata, "name", pltname);
 		Py_DECREF(pltname);
 
 		stroid = DatumGetCString(DirectFunctionCall1(oidout,
 													 ObjectIdGetDatum(tdata->tg_relation->rd_id)));
-		pltrelid = PyString_FromString(stroid);
+		pltrelid = PLyUnicode_FromString(stroid);
 		PyDict_SetItemString(pltdata, "relid", pltrelid);
 		Py_DECREF(pltrelid);
 		pfree(stroid);
 
 		stroid = SPI_getrelname(tdata->tg_relation);
-		plttablename = PyString_FromString(stroid);
+		plttablename = PLyUnicode_FromString(stroid);
 		PyDict_SetItemString(pltdata, "table_name", plttablename);
 		Py_DECREF(plttablename);
 		pfree(stroid);
 
 		stroid = SPI_getnspname(tdata->tg_relation);
-		plttableschema = PyString_FromString(stroid);
+		plttableschema = PLyUnicode_FromString(stroid);
 		PyDict_SetItemString(pltdata, "table_schema", plttableschema);
 		Py_DECREF(plttableschema);
 		pfree(stroid);
 
 		if (TRIGGER_FIRED_BEFORE(tdata->tg_event))
-			pltwhen = PyString_FromString("BEFORE");
+			pltwhen = PLyUnicode_FromString("BEFORE");
 		else if (TRIGGER_FIRED_AFTER(tdata->tg_event))
-			pltwhen = PyString_FromString("AFTER");
+			pltwhen = PLyUnicode_FromString("AFTER");
 		else if (TRIGGER_FIRED_INSTEAD(tdata->tg_event))
-			pltwhen = PyString_FromString("INSTEAD OF");
+			pltwhen = PLyUnicode_FromString("INSTEAD OF");
 		else
 		{
 			elog(ERROR, "unrecognized WHEN tg_event: %u", tdata->tg_event);
@@ -739,7 +737,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 
 		if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
 		{
-			pltlevel = PyString_FromString("ROW");
+			pltlevel = PLyUnicode_FromString("ROW");
 			PyDict_SetItemString(pltdata, "level", pltlevel);
 			Py_DECREF(pltlevel);
 
@@ -750,7 +748,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 
 			if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
 			{
-				pltevent = PyString_FromString("INSERT");
+				pltevent = PLyUnicode_FromString("INSERT");
 
 				PyDict_SetItemString(pltdata, "old", Py_None);
 				pytnew = PLy_input_from_tuple(&proc->result_in,
@@ -763,7 +761,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 			}
 			else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
 			{
-				pltevent = PyString_FromString("DELETE");
+				pltevent = PLyUnicode_FromString("DELETE");
 
 				PyDict_SetItemString(pltdata, "new", Py_None);
 				pytold = PLy_input_from_tuple(&proc->result_in,
@@ -776,7 +774,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 			}
 			else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
 			{
-				pltevent = PyString_FromString("UPDATE");
+				pltevent = PLyUnicode_FromString("UPDATE");
 
 				pytnew = PLy_input_from_tuple(&proc->result_in,
 											  tdata->tg_newtuple,
@@ -803,7 +801,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 		}
 		else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event))
 		{
-			pltlevel = PyString_FromString("STATEMENT");
+			pltlevel = PLyUnicode_FromString("STATEMENT");
 			PyDict_SetItemString(pltdata, "level", pltlevel);
 			Py_DECREF(pltlevel);
 
@@ -812,13 +810,13 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 			*rv = NULL;
 
 			if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
-				pltevent = PyString_FromString("INSERT");
+				pltevent = PLyUnicode_FromString("INSERT");
 			else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
-				pltevent = PyString_FromString("DELETE");
+				pltevent = PLyUnicode_FromString("DELETE");
 			else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
-				pltevent = PyString_FromString("UPDATE");
+				pltevent = PLyUnicode_FromString("UPDATE");
 			else if (TRIGGER_FIRED_BY_TRUNCATE(tdata->tg_event))
-				pltevent = PyString_FromString("TRUNCATE");
+				pltevent = PLyUnicode_FromString("TRUNCATE");
 			else
 			{
 				elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
@@ -847,7 +845,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *r
 			}
 			for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
 			{
-				pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]);
+				pltarg = PLyUnicode_FromString(tdata->tg_trigger->tgargs[i]);
 
 				/*
 				 * stolen, don't Py_DECREF
@@ -931,9 +929,7 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
 			PLyObToDatum *att;
 
 			platt = PyList_GetItem(plkeys, i);
-			if (PyString_Check(platt))
-				plattstr = PyString_AsString(platt);
-			else if (PyUnicode_Check(platt))
+			if (PyUnicode_Check(platt))
 				plattstr = PLyUnicode_AsString(platt);
 			else
 			{
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index 3eedaa80da7..0bce1064951 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -28,27 +28,13 @@
  * exported functions
  */
 
-#if PY_MAJOR_VERSION >= 3
-/* Use separate names to reduce confusion */
-#define plpython_validator plpython3_validator
-#define plpython_call_handler plpython3_call_handler
-#define plpython_inline_handler plpython3_inline_handler
-#endif
-
 extern void _PG_init(void);
 
 PG_MODULE_MAGIC;
 
-PG_FUNCTION_INFO_V1(plpython_validator);
-PG_FUNCTION_INFO_V1(plpython_call_handler);
-PG_FUNCTION_INFO_V1(plpython_inline_handler);
-
-#if PY_MAJOR_VERSION < 3
-/* Define aliases plpython2_call_handler etc */
-PG_FUNCTION_INFO_V1(plpython2_validator);
-PG_FUNCTION_INFO_V1(plpython2_call_handler);
-PG_FUNCTION_INFO_V1(plpython2_inline_handler);
-#endif
+PG_FUNCTION_INFO_V1(plpython3_validator);
+PG_FUNCTION_INFO_V1(plpython3_call_handler);
+PG_FUNCTION_INFO_V1(plpython3_inline_handler);
 
 
 static bool PLy_procedure_is_trigger(Form_pg_proc procStruct);
@@ -82,6 +68,10 @@ _PG_init(void)
 	 * the actual failure for later, so that operations like pg_restore can
 	 * load more than one plpython library so long as they don't try to do
 	 * anything much with the language.
+	 *
+	 * While we only support Python 3 these days, somebody might create an
+	 * out-of-tree version adding back support for Python 2. Conflicts with
+	 * such an extension should be detected.
 	 */
 	bitmask_ptr = (int **) find_rendezvous_variable("plpython_version_bitmask");
 	if (!(*bitmask_ptr))		/* am I the first? */
@@ -125,13 +115,9 @@ PLy_initialize(void)
 	if (inited)
 		return;
 
-#if PY_MAJOR_VERSION >= 3
 	PyImport_AppendInittab("plpy", PyInit_plpy);
-#endif
 	Py_Initialize();
-#if PY_MAJOR_VERSION >= 3
 	PyImport_ImportModule("plpy");
-#endif
 	PLy_init_interp();
 	PLy_init_plpy();
 	if (PyErr_Occurred())
@@ -171,7 +157,7 @@ PLy_init_interp(void)
 }
 
 Datum
-plpython_validator(PG_FUNCTION_ARGS)
+plpython3_validator(PG_FUNCTION_ARGS)
 {
 	Oid			funcoid = PG_GETARG_OID(0);
 	HeapTuple	tuple;
@@ -203,17 +189,8 @@ plpython_validator(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-#if PY_MAJOR_VERSION < 3
 Datum
-plpython2_validator(PG_FUNCTION_ARGS)
-{
-	/* call plpython validator with our fcinfo so it gets our oid */
-	return plpython_validator(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
-Datum
-plpython_call_handler(PG_FUNCTION_ARGS)
+plpython3_call_handler(PG_FUNCTION_ARGS)
 {
 	bool		nonatomic;
 	Datum		retval;
@@ -284,16 +261,8 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 	return retval;
 }
 
-#if PY_MAJOR_VERSION < 3
 Datum
-plpython2_call_handler(PG_FUNCTION_ARGS)
-{
-	return plpython_call_handler(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
-Datum
-plpython_inline_handler(PG_FUNCTION_ARGS)
+plpython3_inline_handler(PG_FUNCTION_ARGS)
 {
 	LOCAL_FCINFO(fake_fcinfo, 0);
 	InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
@@ -368,14 +337,6 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
 	PG_RETURN_VOID();
 }
 
-#if PY_MAJOR_VERSION < 3
-Datum
-plpython2_inline_handler(PG_FUNCTION_ARGS)
-{
-	return plpython_inline_handler(fcinfo);
-}
-#endif							/* PY_MAJOR_VERSION < 3 */
-
 static bool
 PLy_procedure_is_trigger(Form_pg_proc procStruct)
 {
diff --git a/src/pl/plpython/plpy_planobject.c b/src/pl/plpython/plpy_planobject.c
index 5951d2a6ff5..ec2439c6a1f 100644
--- a/src/pl/plpython/plpy_planobject.c
+++ b/src/pl/plpython/plpy_planobject.c
@@ -119,7 +119,7 @@ PLy_plan_status(PyObject *self, PyObject *args)
 	{
 		Py_INCREF(Py_True);
 		return Py_True;
-		/* return PyInt_FromLong(self->status); */
+		/* return PyLong_FromLong(self->status); */
 	}
 	return NULL;
 }
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
index 907f89d1535..fa08f0dbfb3 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -107,7 +107,6 @@ static PyMethodDef PLy_exc_methods[] = {
 	{NULL, NULL, 0, NULL}
 };
 
-#if PY_MAJOR_VERSION >= 3
 static PyModuleDef PLy_module = {
 	PyModuleDef_HEAD_INIT,
 	.m_name = "plpy",
@@ -139,7 +138,6 @@ PyInit_plpy(void)
 
 	return m;
 }
-#endif							/* PY_MAJOR_VERSION >= 3 */
 
 void
 PLy_init_plpy(void)
@@ -148,10 +146,6 @@ PLy_init_plpy(void)
 			   *main_dict,
 			   *plpy_mod;
 
-#if PY_MAJOR_VERSION < 3
-	PyObject   *plpy;
-#endif
-
 	/*
 	 * initialize plpy module
 	 */
@@ -160,13 +154,7 @@ PLy_init_plpy(void)
 	PLy_subtransaction_init_type();
 	PLy_cursor_init_type();
 
-#if PY_MAJOR_VERSION >= 3
 	PyModule_Create(&PLy_module);
-	/* for Python 3 we initialized the exceptions in PyInit_plpy */
-#else
-	plpy = Py_InitModule("plpy", PLy_methods);
-	PLy_add_exceptions(plpy);
-#endif
 
 	/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
 
@@ -189,11 +177,7 @@ PLy_add_exceptions(PyObject *plpy)
 	PyObject   *excmod;
 	HASHCTL		hash_ctl;
 
-#if PY_MAJOR_VERSION < 3
-	excmod = Py_InitModule("spiexceptions", PLy_exc_methods);
-#else
 	excmod = PyModule_Create(&PLy_exc_module);
-#endif
 	if (excmod == NULL)
 		PLy_elog(ERROR, "could not create the spiexceptions module");
 
@@ -268,7 +252,7 @@ PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
 		if (dict == NULL)
 			PLy_elog(ERROR, NULL);
 
-		sqlstate = PyString_FromString(unpack_sql_state(exception_map[i].sqlstate));
+		sqlstate = PLyUnicode_FromString(unpack_sql_state(exception_map[i].sqlstate));
 		if (sqlstate == NULL)
 			PLy_elog(ERROR, "could not generate SPI exceptions");
 
@@ -346,7 +330,7 @@ PLy_quote_literal(PyObject *self, PyObject *args)
 		return NULL;
 
 	quoted = quote_literal_cstr(str);
-	ret = PyString_FromString(quoted);
+	ret = PLyUnicode_FromString(quoted);
 	pfree(quoted);
 
 	return ret;
@@ -363,10 +347,10 @@ PLy_quote_nullable(PyObject *self, PyObject *args)
 		return NULL;
 
 	if (str == NULL)
-		return PyString_FromString("NULL");
+		return PLyUnicode_FromString("NULL");
 
 	quoted = quote_literal_cstr(str);
-	ret = PyString_FromString(quoted);
+	ret = PLyUnicode_FromString(quoted);
 	pfree(quoted);
 
 	return ret;
@@ -383,7 +367,7 @@ PLy_quote_ident(PyObject *self, PyObject *args)
 		return NULL;
 
 	quoted = quote_identifier(str);
-	ret = PyString_FromString(quoted);
+	ret = PLyUnicode_FromString(quoted);
 
 	return ret;
 }
@@ -400,7 +384,7 @@ object_to_string(PyObject *obj)
 		{
 			char	   *str;
 
-			str = pstrdup(PyString_AsString(so));
+			str = pstrdup(PLyUnicode_AsString(so));
 			Py_DECREF(so);
 
 			return str;
@@ -444,7 +428,7 @@ PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
 	else
 		so = PyObject_Str(args);
 
-	if (so == NULL || ((message = PyString_AsString(so)) == NULL))
+	if (so == NULL || ((message = PLyUnicode_AsString(so)) == NULL))
 	{
 		level = ERROR;
 		message = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
@@ -457,7 +441,7 @@ PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
 	{
 		while (PyDict_Next(kw, &pos, &key, &value))
 		{
-			char	   *keyword = PyString_AsString(key);
+			char	   *keyword = PLyUnicode_AsString(key);
 
 			if (strcmp(keyword, "message") == 0)
 			{
diff --git a/src/pl/plpython/plpy_plpymodule.h b/src/pl/plpython/plpy_plpymodule.h
index 54d78101ceb..ad6436aca78 100644
--- a/src/pl/plpython/plpy_plpymodule.h
+++ b/src/pl/plpython/plpy_plpymodule.h
@@ -11,9 +11,7 @@
 extern HTAB *PLy_spi_exceptions;
 
 
-#if PY_MAJOR_VERSION >= 3
 PyMODINIT_FUNC PyInit_plpy(void);
-#endif
 extern void PLy_init_plpy(void);
 
 #endif							/* PLPY_PLPYMODULE_H */
diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c
index 54f39419c84..a8516b2db30 100644
--- a/src/pl/plpython/plpy_resultobject.c
+++ b/src/pl/plpython/plpy_resultobject.c
@@ -76,7 +76,7 @@ PLy_result_new(void)
 
 	Py_INCREF(Py_None);
 	ob->status = Py_None;
-	ob->nrows = PyInt_FromLong(-1);
+	ob->nrows = PyLong_FromLong(-1);
 	ob->rows = PyList_New(0);
 	ob->tupdesc = NULL;
 	if (!ob->rows)
@@ -125,7 +125,7 @@ PLy_result_colnames(PyObject *self, PyObject *unused)
 	{
 		Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
 
-		PyList_SET_ITEM(list, i, PyString_FromString(NameStr(attr->attname)));
+		PyList_SET_ITEM(list, i, PLyUnicode_FromString(NameStr(attr->attname)));
 	}
 
 	return list;
@@ -151,7 +151,7 @@ PLy_result_coltypes(PyObject *self, PyObject *unused)
 	{
 		Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
 
-		PyList_SET_ITEM(list, i, PyInt_FromLong(attr->atttypid));
+		PyList_SET_ITEM(list, i, PyLong_FromLong(attr->atttypid));
 	}
 
 	return list;
@@ -177,7 +177,7 @@ PLy_result_coltypmods(PyObject *self, PyObject *unused)
 	{
 		Form_pg_attribute attr = TupleDescAttr(ob->tupdesc, i);
 
-		PyList_SET_ITEM(list, i, PyInt_FromLong(attr->atttypmod));
+		PyList_SET_ITEM(list, i, PyLong_FromLong(attr->atttypmod));
 	}
 
 	return list;
@@ -226,19 +226,11 @@ PLy_result_str(PyObject *arg)
 {
 	PLyResultObject *ob = (PLyResultObject *) arg;
 
-#if PY_MAJOR_VERSION >= 3
 	return PyUnicode_FromFormat("<%s status=%S nrows=%S rows=%S>",
 								Py_TYPE(ob)->tp_name,
 								ob->status,
 								ob->nrows,
 								ob->rows);
-#else
-	return PyString_FromFormat("<%s status=%ld nrows=%ld rows=%s>",
-							   ob->ob_type->tp_name,
-							   PyInt_AsLong(ob->status),
-							   PyInt_AsLong(ob->nrows),
-							   PyString_AsString(PyObject_Str(ob->rows)));
-#endif
 }
 
 static PyObject *
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 86d70470a74..9a71a42c15f 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -90,9 +90,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
 			int32		typmod;
 
 			optr = PySequence_GetItem(list, i);
-			if (PyString_Check(optr))
-				sptr = PyString_AsString(optr);
-			else if (PyUnicode_Check(optr))
+			if (PyUnicode_Check(optr))
 				sptr = PLyUnicode_AsString(optr);
 			else
 			{
@@ -186,7 +184,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
 
 	if (list != NULL)
 	{
-		if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
+		if (!PySequence_Check(list) || PyUnicode_Check(list))
 		{
 			PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
 			return NULL;
@@ -205,7 +203,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
 
 		if (!so)
 			PLy_elog(ERROR, "could not execute plan");
-		sv = PyString_AsString(so);
+		sv = PLyUnicode_AsString(so);
 		PLy_exception_set_plural(PyExc_TypeError,
 								 "Expected sequence of %d argument, got %d: %s",
 								 "Expected sequence of %d arguments, got %d: %s",
@@ -360,7 +358,7 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
 		return NULL;
 	}
 	Py_DECREF(result->status);
-	result->status = PyInt_FromLong(status);
+	result->status = PyLong_FromLong(status);
 
 	if (status > 0 && tuptable == NULL)
 	{
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 5e807b139f1..bc348473d36 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -26,8 +26,8 @@ static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d);
-static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d);
-static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyLong_FromInt16(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyLong_FromInt32(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyLong_FromOid(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d);
@@ -517,10 +517,10 @@ PLy_input_setup_func(PLyDatumToOb *arg, MemoryContext arg_mcxt,
 				arg->func = PLyDecimal_FromNumeric;
 				break;
 			case INT2OID:
-				arg->func = PLyInt_FromInt16;
+				arg->func = PLyLong_FromInt16;
 				break;
 			case INT4OID:
-				arg->func = PLyInt_FromInt32;
+				arg->func = PLyLong_FromInt32;
 				break;
 			case INT8OID:
 				arg->func = PLyLong_FromInt64;
@@ -600,15 +600,15 @@ PLyDecimal_FromNumeric(PLyDatumToOb *arg, Datum d)
 }
 
 static PyObject *
-PLyInt_FromInt16(PLyDatumToOb *arg, Datum d)
+PLyLong_FromInt16(PLyDatumToOb *arg, Datum d)
 {
-	return PyInt_FromLong(DatumGetInt16(d));
+	return PyLong_FromLong(DatumGetInt16(d));
 }
 
 static PyObject *
-PLyInt_FromInt32(PLyDatumToOb *arg, Datum d)
+PLyLong_FromInt32(PLyDatumToOb *arg, Datum d)
 {
-	return PyInt_FromLong(DatumGetInt32(d));
+	return PyLong_FromLong(DatumGetInt32(d));
 }
 
 static PyObject *
@@ -641,7 +641,7 @@ static PyObject *
 PLyString_FromScalar(PLyDatumToOb *arg, Datum d)
 {
 	char	   *x = OutputFunctionCall(&arg->u.scalar.typfunc, d);
-	PyObject   *r = PyString_FromString(x);
+	PyObject   *r = PLyUnicode_FromString(x);
 
 	pfree(x);
 	return r;
@@ -954,7 +954,7 @@ PLyObject_ToComposite(PLyObToDatum *arg, PyObject *plrv,
 	 * The string conversion case doesn't require a tupdesc, nor per-field
 	 * conversion data, so just go for it if that's the case to use.
 	 */
-	if (PyString_Check(plrv) || PyUnicode_Check(plrv))
+	if (PyUnicode_Check(plrv))
 		return PLyString_ToComposite(arg, plrv, inarray);
 
 	/*
@@ -1032,25 +1032,17 @@ PLyObject_AsString(PyObject *plrv)
 	else if (PyFloat_Check(plrv))
 	{
 		/* use repr() for floats, str() is lossy */
-#if PY_MAJOR_VERSION >= 3
 		PyObject   *s = PyObject_Repr(plrv);
 
 		plrv_bo = PLyUnicode_Bytes(s);
 		Py_XDECREF(s);
-#else
-		plrv_bo = PyObject_Repr(plrv);
-#endif
 	}
 	else
 	{
-#if PY_MAJOR_VERSION >= 3
 		PyObject   *s = PyObject_Str(plrv);
 
 		plrv_bo = PLyUnicode_Bytes(s);
 		Py_XDECREF(s);
-#else
-		plrv_bo = PyObject_Str(plrv);
-#endif
 	}
 	if (!plrv_bo)
 		PLy_elog(ERROR, "could not create string representation of Python object");
diff --git a/src/pl/plpython/plpy_util.c b/src/pl/plpython/plpy_util.c
index 4a7d7264d79..22e2a599ad9 100644
--- a/src/pl/plpython/plpy_util.c
+++ b/src/pl/plpython/plpy_util.c
@@ -78,12 +78,6 @@ PLyUnicode_Bytes(PyObject *unicode)
  * Convert a Python unicode object to a C string in PostgreSQL server
  * encoding.  No Python object reference is passed out of this
  * function.  The result is palloc'ed.
- *
- * Note that this function is disguised as PyString_AsString() when
- * using Python 3.  That function returns a pointer into the internal
- * memory of the argument, which isn't exactly the interface of this
- * function.  But in either case you get a rather short-lived
- * reference that you ought to better leave alone.
  */
 char *
 PLyUnicode_AsString(PyObject *unicode)
@@ -95,7 +89,6 @@ PLyUnicode_AsString(PyObject *unicode)
 	return rv;
 }
 
-#if PY_MAJOR_VERSION >= 3
 /*
  * Convert a C string in the PostgreSQL server encoding to a Python
  * unicode object.  Reference ownership is passed to the caller.
@@ -126,5 +119,3 @@ PLyUnicode_FromString(const char *s)
 {
 	return PLyUnicode_FromStringAndSize(s, strlen(s));
 }
-
-#endif							/* PY_MAJOR_VERSION >= 3 */
diff --git a/src/pl/plpython/plpy_util.h b/src/pl/plpython/plpy_util.h
index c9ba7edc0ec..7c6577925ea 100644
--- a/src/pl/plpython/plpy_util.h
+++ b/src/pl/plpython/plpy_util.h
@@ -11,9 +11,7 @@
 extern PyObject *PLyUnicode_Bytes(PyObject *unicode);
 extern char *PLyUnicode_AsString(PyObject *unicode);
 
-#if PY_MAJOR_VERSION >= 3
 extern PyObject *PLyUnicode_FromString(const char *s);
 extern PyObject *PLyUnicode_FromStringAndSize(const char *s, Py_ssize_t size);
-#endif
 
 #endif							/* PLPY_UTIL_H */
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 05e4362dab9..2a0c9bf0361 100644
--- a/src/pl/plpython/plpython.h
+++ b/src/pl/plpython/plpython.h
@@ -59,37 +59,6 @@
 #include <Python.h>
 #endif
 
-/*
- * Python 2/3 strings/unicode/bytes handling.  Python 2 has strings
- * and unicode, Python 3 has strings, which are unicode on the C
- * level, and bytes.  The porting convention, which is similarly used
- * in Python 2.6, is that "Unicode" is always unicode, and "Bytes" are
- * bytes in Python 3 and strings in Python 2.  Since we keep
- * supporting Python 2 and its usual strings, we provide a
- * compatibility layer for Python 3 that when asked to convert a C
- * string to a Python string it converts the C string from the
- * PostgreSQL server encoding to a Python Unicode object.
- */
-#if PY_MAJOR_VERSION >= 3
-#define PyString_Check(x) 0
-#define PyString_AsString(x) PLyUnicode_AsString(x)
-#define PyString_FromString(x) PLyUnicode_FromString(x)
-#define PyString_FromStringAndSize(x, size) PLyUnicode_FromStringAndSize(x, size)
-#endif
-
-/*
- * Python 3 only has long.
- */
-#if PY_MAJOR_VERSION >= 3
-#define PyInt_FromLong(x) PyLong_FromLong(x)
-#define PyInt_AsLong(x) PyLong_AsLong(x)
-#endif
-
-/* Python 3 removed the Py_TPFLAGS_HAVE_ITER flag */
-#if PY_MAJOR_VERSION >= 3
-#define Py_TPFLAGS_HAVE_ITER 0
-#endif
-
 /* define our text domain for translations */
 #undef TEXTDOMAIN
 #define TEXTDOMAIN PG_TEXTDOMAIN("plpython")
@@ -130,8 +99,7 @@
 #define printf(...)		pg_printf(__VA_ARGS__)
 
 /*
- * Used throughout, and also by the Python 2/3 porting layer, so it's easier to
- * just include it everywhere.
+ * Used throughout, so it's easier to just include it everywhere.
  */
 #include "plpy_util.h"
 
diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c
index 39bad558023..889ece315df 100644
--- a/contrib/hstore_plpython/hstore_plpython.c
+++ b/contrib/hstore_plpython/hstore_plpython.c
@@ -12,10 +12,8 @@ extern void _PG_init(void);
 /* Linkage to functions in plpython module */
 typedef char *(*PLyObject_AsString_t) (PyObject *plrv);
 static PLyObject_AsString_t PLyObject_AsString_p;
-#if PY_MAJOR_VERSION >= 3
 typedef PyObject *(*PLyUnicode_FromStringAndSize_t) (const char *s, Py_ssize_t size);
 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
-#endif
 
 /* Linkage to functions in hstore module */
 typedef HStore *(*hstoreUpgrade_t) (Datum orig);
@@ -41,12 +39,10 @@ _PG_init(void)
 	PLyObject_AsString_p = (PLyObject_AsString_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
 							   true, NULL);
-#if PY_MAJOR_VERSION >= 3
 	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-#endif
 	AssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
 	hstoreUpgrade_p = (hstoreUpgrade_t)
 		load_external_function("$libdir/hstore", "hstoreUpgrade",
@@ -102,16 +98,16 @@ hstore_to_plpython(PG_FUNCTION_ARGS)
 	{
 		PyObject   *key;
 
-		key = PyString_FromStringAndSize(HSTORE_KEY(entries, base, i),
-										 HSTORE_KEYLEN(entries, i));
+		key = PLyUnicode_FromStringAndSize(HSTORE_KEY(entries, base, i),
+										  HSTORE_KEYLEN(entries, i));
 		if (HSTORE_VALISNULL(entries, i))
 			PyDict_SetItem(dict, key, Py_None);
 		else
 		{
 			PyObject   *value;
 
-			value = PyString_FromStringAndSize(HSTORE_VAL(entries, base, i),
-											   HSTORE_VALLEN(entries, i));
+			value = PLyUnicode_FromStringAndSize(HSTORE_VAL(entries, base, i),
+												HSTORE_VALLEN(entries, i));
 			PyDict_SetItem(dict, key, value);
 			Py_XDECREF(value);
 		}
diff --git a/contrib/jsonb_plpython/jsonb_plpython.c b/contrib/jsonb_plpython/jsonb_plpython.c
index 836c1787706..e6a87e557a9 100644
--- a/contrib/jsonb_plpython/jsonb_plpython.c
+++ b/contrib/jsonb_plpython/jsonb_plpython.c
@@ -28,11 +28,9 @@ static PyObject *PLyObject_FromJsonbContainer(JsonbContainer *jsonb);
 static JsonbValue *PLyObject_ToJsonbValue(PyObject *obj,
 										  JsonbParseState **jsonb_state, bool is_elem);
 
-#if PY_MAJOR_VERSION >= 3
 typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
 			(const char *s, Py_ssize_t size);
 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
-#endif
 
 /*
  * Module initialize function: fetch function pointers for cross-module calls.
@@ -45,13 +43,10 @@ _PG_init(void)
 	PLyObject_AsString_p = (PLyObject_AsString_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
 							   true, NULL);
-#if PY_MAJOR_VERSION >= 3
 	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-#endif
-
 	AssertVariableIsOfType(&PLy_elog_impl, PLy_elog_impl_t);
 	PLy_elog_impl_p = (PLy_elog_impl_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLy_elog_impl",
@@ -74,7 +69,7 @@ PLyString_FromJsonbValue(JsonbValue *jbv)
 {
 	Assert(jbv->type == jbvString);
 
-	return PyString_FromStringAndSize(jbv->val.string.val, jbv->val.string.len);
+	return PLyUnicode_FromStringAndSize(jbv->val.string.val, jbv->val.string.len);
 }
 
 /*
@@ -415,7 +410,7 @@ PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_ele
 {
 	JsonbValue *out;
 
-	if (!(PyString_Check(obj) || PyUnicode_Check(obj)))
+	if (!PyUnicode_Check(obj))
 	{
 		if (PySequence_Check(obj))
 			return PLySequence_ToJsonbValue(obj, jsonb_state);
@@ -427,7 +422,7 @@ PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_ele
 
 	if (obj == Py_None)
 		out->type = jbvNull;
-	else if (PyString_Check(obj) || PyUnicode_Check(obj))
+	else if (PyUnicode_Check(obj))
 		PLyString_ToJsonbValue(obj, out);
 
 	/*
diff --git a/contrib/ltree_plpython/ltree_plpython.c b/contrib/ltree_plpython/ltree_plpython.c
index 1570e77dd9f..7431a1150a9 100644
--- a/contrib/ltree_plpython/ltree_plpython.c
+++ b/contrib/ltree_plpython/ltree_plpython.c
@@ -9,10 +9,8 @@ PG_MODULE_MAGIC;
 extern void _PG_init(void);
 
 /* Linkage to functions in plpython module */
-#if PY_MAJOR_VERSION >= 3
 typedef PyObject *(*PLyUnicode_FromStringAndSize_t) (const char *s, Py_ssize_t size);
 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
-#endif
 
 
 /*
@@ -22,12 +20,10 @@ void
 _PG_init(void)
 {
 	/* Asserts verify that typedefs above match original declarations */
-#if PY_MAJOR_VERSION >= 3
 	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-#endif
 }
 
 
@@ -54,7 +50,7 @@ ltree_to_plpython(PG_FUNCTION_ARGS)
 	curlevel = LTREE_FIRST(in);
 	for (i = 0; i < in->numlevel; i++)
 	{
-		PyList_SetItem(list, i, PyString_FromStringAndSize(curlevel->name, curlevel->len));
+		PyList_SetItem(list, i, PLyUnicode_FromStringAndSize(curlevel->name, curlevel->len));
 		curlevel = LEVEL_NEXT(curlevel);
 	}
 
-- 
2.35.1.354.g715d08a9e5

v7-0004-plpython-Adjust-docs-after-removal-of-Python-2-su.patchtext/x-diff; charset=us-asciiDownload
From fc32d0b87e5ebca1bf737342605ce7fd4b4ac32b Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sun, 6 Mar 2022 16:57:28 -0800
Subject: [PATCH v7 4/4] plpython: Adjust docs after removal of Python 2
 support.

Reviewed-By: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/20211031184548.g4sxfe47n2kyi55r@alap3.anarazel.de
---
 doc/src/sgml/hstore.sgml               |   8 +-
 doc/src/sgml/json.sgml                 |   9 +-
 doc/src/sgml/ltree.sgml                |  10 +-
 doc/src/sgml/plpython.sgml             | 289 +++++--------------------
 doc/src/sgml/ref/comment.sgml          |   2 +-
 doc/src/sgml/ref/create_transform.sgml |   6 +-
 doc/src/sgml/ref/drop_transform.sgml   |   4 +-
 7 files changed, 71 insertions(+), 257 deletions(-)

diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 870063c288b..679878b3afc 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -943,12 +943,8 @@ ALTER TABLE tablename ALTER hstorecol TYPE hstore USING hstorecol || '';
    and <literal>hstore_plperlu</literal>, for trusted and untrusted PL/Perl.
    If you install these transforms and specify them when creating a
    function, <type>hstore</type> values are mapped to Perl hashes.  The
-   extensions for PL/Python are
-   called <literal>hstore_plpythonu</literal>, <literal>hstore_plpython2u</literal>,
-   and <literal>hstore_plpython3u</literal>
-   (see <xref linkend="plpython-python23"/> for the PL/Python naming
-   convention).  If you use them, <type>hstore</type> values are mapped to
-   Python dictionaries.
+   extension for PL/Python is called <literal>hstore_plpython3u</literal>.
+   If you use it, <type>hstore</type> values are mapped to Python dictionaries.
   </para>
 
   <caution>
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 673c70c3bb8..c4223fafb60 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -716,12 +716,9 @@ UPDATE table_name SET jsonb_field[1]['a'] = '1';
   </para>
 
   <para>
-   The extensions for PL/Python are called <literal>jsonb_plpythonu</literal>,
-   <literal>jsonb_plpython2u</literal>, and
-   <literal>jsonb_plpython3u</literal> (see <xref
-   linkend="plpython-python23"/> for the PL/Python naming convention).  If you
-   use them, <type>jsonb</type> values are mapped to Python dictionaries,
-   lists, and scalars, as appropriate.
+   The extension for PL/Python is called <literal>jsonb_plpython3u</literal>.
+   If you use it, <type>jsonb</type> values are mapped to Python
+   dictionaries, lists, and scalars, as appropriate.
   </para>
 
   <para>
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index 436be76bfaa..508f404ae81 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -826,19 +826,15 @@ ltreetest=&gt; SELECT ins_label(path,2,'Space') FROM test WHERE path &lt;@ 'Top.
   <title>Transforms</title>
 
   <para>
-   Additional extensions are available that implement transforms for
-   the <type>ltree</type> type for PL/Python.  The extensions are
-   called <literal>ltree_plpythonu</literal>, <literal>ltree_plpython2u</literal>,
-   and <literal>ltree_plpython3u</literal>
-   (see <xref linkend="plpython-python23"/> for the PL/Python naming
-   convention).  If you install these transforms and specify them when
+   The <literal>ltree_plpython3u</literal> extension implements transforms for
+   the <type>ltree</type> type for PL/Python. If installed and specified when
    creating a function, <type>ltree</type> values are mapped to Python lists.
    (The reverse is currently not supported, however.)
   </para>
 
   <caution>
    <para>
-    It is strongly recommended that the transform extensions be installed in
+    It is strongly recommended that the transform extension be installed in
     the same schema as <filename>ltree</filename>.  Otherwise there are
     installation-time security hazards if a transform extension's schema
     contains objects defined by a hostile user.
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index b67f8f4aaed..54355effd71 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -14,8 +14,7 @@
 
  <para>
   To install PL/Python in a particular database, use
-  <literal>CREATE EXTENSION plpythonu</literal> (but
-  see also <xref linkend="plpython-python23"/>).
+  <literal>CREATE EXTENSION plpython3u</literal>.
  </para>
 
   <tip>
@@ -28,14 +27,14 @@
  <para>
   PL/Python is only available as an <quote>untrusted</quote> language, meaning
   it does not offer any way of restricting what users can do in it and
-  is therefore named <literal>plpythonu</literal>.  A trusted
+  is therefore named <literal>plpython3u</literal>.  A trusted
   variant <literal>plpython</literal> might become available in the future
   if a secure execution mechanism is developed in Python.  The
   writer of a function in untrusted PL/Python must take care that the
   function cannot be used to do anything unwanted, since it will be
   able to do anything that could be done by a user logged in as the
   database administrator.  Only superusers can create functions in
-  untrusted languages such as <literal>plpythonu</literal>.
+  untrusted languages such as <literal>plpython3u</literal>.
  </para>
 
  <note>
@@ -47,140 +46,6 @@
   </para>
  </note>
 
- <sect1 id="plpython-python23">
-  <title>Python 2 vs. Python 3</title>
-
-  <para>
-   PL/Python supports both the Python 2 and Python 3 language
-   variants.  (The PostgreSQL installation instructions might contain
-   more precise information about the exact supported minor versions
-   of Python.)  Because the Python 2 and Python 3 language variants
-   are incompatible in some important aspects, the following naming
-   and transitioning scheme is used by PL/Python to avoid mixing them:
-
-   <itemizedlist>
-    <listitem>
-     <para>
-      The PostgreSQL language named <literal>plpython2u</literal>
-      implements PL/Python based on the Python 2 language variant.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The PostgreSQL language named <literal>plpython3u</literal>
-      implements PL/Python based on the Python 3 language variant.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The language named <literal>plpythonu</literal> implements
-      PL/Python based on the default Python language variant, which is
-      currently Python 2.  (This default is independent of what any
-      local Python installations might consider to be
-      their <quote>default</quote>, for example,
-      what <filename>/usr/bin/python</filename> might be.)  The
-      default will probably be changed to Python 3 in a distant future
-      release of PostgreSQL, depending on the progress of the
-      migration to Python 3 in the Python community.
-     </para>
-    </listitem>
-   </itemizedlist>
-
-   This scheme is analogous to the recommendations in <ulink
-   url="https://www.python.org/dev/peps/pep-0394/">PEP 394</ulink> regarding the
-   naming and transitioning of the <command>python</command> command.
-  </para>
-
-  <para>
-   It depends on the build configuration or the installed packages
-   whether PL/Python for Python 2 or Python 3 or both are available.
-  </para>
-
-  <tip>
-   <para>
-    The built variant depends on which Python version was found during
-    the installation or which version was explicitly set using
-    the <envar>PYTHON</envar> environment variable;
-    see <xref linkend="install-procedure"/>.  To make both variants of
-    PL/Python available in one installation, the source tree has to be
-    configured and built twice.
-   </para>
-  </tip>
-
-  <para>
-   This results in the following usage and migration strategy:
-
-   <itemizedlist>
-    <listitem>
-     <para>
-      Existing users and users who are currently not interested in
-      Python 3 use the language name <literal>plpythonu</literal> and
-      don't have to change anything for the foreseeable future.  It is
-      recommended to gradually <quote>future-proof</quote> the code
-      via migration to Python 2.6/2.7 to simplify the eventual
-      migration to Python 3.
-     </para>
-
-     <para>
-      In practice, many PL/Python functions will migrate to Python 3
-      with few or no changes.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      Users who know that they have heavily Python 2 dependent code
-      and don't plan to ever change it can make use of
-      the <literal>plpython2u</literal> language name.  This will
-      continue to work into the very distant future, until Python 2
-      support might be completely dropped by PostgreSQL.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      Users who want to dive into Python 3 can use
-      the <literal>plpython3u</literal> language name, which will keep
-      working forever by today's standards.  In the distant future,
-      when Python 3 might become the default, they might like to
-      remove the <quote>3</quote> for aesthetic reasons.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      Daredevils, who want to build a Python-3-only operating system
-      environment, can change the contents of
-      <literal>plpythonu</literal>'s extension control and script files
-      to make <literal>plpythonu</literal> be equivalent
-      to <literal>plpython3u</literal>, keeping in mind that this
-      would make their installation incompatible with most of the rest
-      of the world.
-     </para>
-    </listitem>
-   </itemizedlist>
-  </para>
-
-  <para>
-   See also the
-   document <ulink url="https://docs.python.org/3/whatsnew/3.0.html">What's
-   New In Python 3.0</ulink> for more information about porting to
-   Python 3.
-  </para>
-
-  <para>
-   It is not allowed to use PL/Python based on Python 2 and PL/Python
-   based on Python 3 in the same session, because the symbols in the
-   dynamic modules would clash, which could result in crashes of the
-   PostgreSQL server process.  There is a check that prevents mixing
-   Python major versions in a session, which will abort the session if
-   a mismatch is detected.  It is possible, however, to use both
-   PL/Python variants in the same database, from separate sessions.
-  </para>
- </sect1>
-
  <sect1 id="plpython-funcs">
   <title>PL/Python Functions</title>
 
@@ -193,7 +58,7 @@ CREATE FUNCTION <replaceable>funcname</replaceable> (<replaceable>argument-list<
   RETURNS <replaceable>return-type</replaceable>
 AS $$
   # PL/Python function body
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
   </para>
 
@@ -225,7 +90,7 @@ AS $$
   if a &gt; b:
     return a
   return b
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
    The Python code that is given as the body of the function definition
@@ -255,7 +120,7 @@ CREATE FUNCTION pystrip(x text)
 AS $$
   x = x.strip()  # error
   return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
    because assigning to <varname>x</varname>
    makes <varname>x</varname> a local variable for the entire block,
@@ -271,7 +136,7 @@ AS $$
   global x
   x = x.strip()  # ok now
   return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
    But it is advisable not to rely on this implementation detail of
    PL/Python.  It is better to treat the function parameters as
@@ -303,11 +168,8 @@ $$ LANGUAGE plpythonu;
 
      <listitem>
       <para>
-       PostgreSQL <type>smallint</type> and <type>int</type> are
-       converted to Python <type>int</type>.
-       PostgreSQL <type>bigint</type> and <type>oid</type> are converted
-       to <type>long</type> in Python 2 and to <type>int</type> in
-       Python 3.
+       PostgreSQL <type>smallint</type>, <type>int</type>, <type>bigint</type>
+       and <type>oid</type> are converted to Python <type>int</type>.
       </para>
      </listitem>
 
@@ -335,19 +197,15 @@ $$ LANGUAGE plpythonu;
 
      <listitem>
       <para>
-       PostgreSQL <type>bytea</type> is converted to
-       Python <type>str</type> in Python 2 and to <type>bytes</type>
-       in Python 3.  In Python 2, the string should be treated as a
-       byte sequence without any character encoding.
+       PostgreSQL <type>bytea</type> is converted to Python <type>bytes</type>.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       All other data types, including the PostgreSQL character string
-       types, are converted to a Python <type>str</type>.  In Python
-       2, this string will be in the PostgreSQL server encoding; in
-       Python 3, it will be a Unicode string like all strings.
+       All other data types, including the PostgreSQL character string types,
+       are converted to a Python <type>str</type> (in Unicode like all Python
+       strings).
       </para>
      </listitem>
 
@@ -375,10 +233,10 @@ $$ LANGUAGE plpythonu;
 
      <listitem>
       <para>
-       When the PostgreSQL return type is <type>bytea</type>, the
-       return value will be converted to a string (Python 2) or bytes
-       (Python 3) using the respective Python built-ins, with the
-       result being converted to <type>bytea</type>.
+       When the PostgreSQL return type is <type>bytea</type>, the return value
+       will be converted to Python <type>bytes</type> using the respective
+       Python built-ins, with the result being converted to
+       <type>bytea</type>.
       </para>
      </listitem>
 
@@ -393,14 +251,8 @@ $$ LANGUAGE plpythonu;
       </para>
 
       <para>
-       Strings in Python 2 are required to be in the PostgreSQL server
-       encoding when they are passed to PostgreSQL.  Strings that are
-       not valid in the current server encoding will raise an error,
-       but not all encoding mismatches can be detected, so garbage
-       data can still result when this is not done correctly.  Unicode
-       strings are converted to the correct encoding automatically, so
-       it can be safer and more convenient to use those.  In Python 3,
-       all strings are Unicode strings.
+       Strings are automatically converted to the PostgreSQL server encoding
+       when they are passed to PostgreSQL.
       </para>
      </listitem>
 
@@ -440,7 +292,7 @@ AS $$
   if a &gt; b:
     return a
   return b
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
    As shown above, to return an SQL null value from a PL/Python
@@ -461,10 +313,10 @@ CREATE FUNCTION return_arr()
   RETURNS int[]
 AS $$
 return [1, 2, 3, 4, 5]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT return_arr();
- return_arr  
+ return_arr
 -------------
  {1,2,3,4,5}
 (1 row)
@@ -479,11 +331,11 @@ SELECT return_arr();
 CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
 plpy.info(x, type(x))
 return x
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
 INFO:  ([[1, 2, 3], [4, 5, 6]], &lt;type 'list'&gt;)
- test_type_conversion_array_int4 
+ test_type_conversion_array_int4
 ---------------------------------
  {{1,2,3},{4,5,6}}
 (1 row)
@@ -506,7 +358,7 @@ CREATE FUNCTION return_str_arr()
   RETURNS varchar[]
 AS $$
 return "hello"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT return_str_arr();
  return_str_arr
@@ -540,7 +392,7 @@ AS $$
   if (e["age"] &lt; 30) and (e["salary"] &gt; 100000):
     return True
   return False
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
   </para>
 
@@ -574,7 +426,7 @@ CREATE FUNCTION make_pair (name text, value integer)
 AS $$
   return ( name, value )
   # or alternatively, as list: return [ name, value ]
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
        To return an SQL null for any column, insert <symbol>None</symbol> at
@@ -600,7 +452,7 @@ CREATE FUNCTION make_pair (name text, value integer)
   RETURNS named_value
 AS $$
   return { "name": name, "value": value }
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
        Any extra dictionary key/value pairs are ignored. Missing keys are
@@ -633,7 +485,7 @@ AS $$
   nv.name = name
   nv.value = value
   return nv
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
       </para>
      </listitem>
@@ -646,7 +498,7 @@ $$ LANGUAGE plpythonu;
 <programlisting>
 CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
 return (1, 2)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_simple();
 </programlisting>
@@ -657,7 +509,7 @@ SELECT * FROM multiout_simple();
 <programlisting>
 CREATE PROCEDURE python_triple(INOUT a integer, INOUT b integer) AS $$
 return (a * 3, b * 3)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CALL python_triple(5, 10);
 </programlisting>
@@ -693,7 +545,7 @@ AS $$
   # return tuple containing lists as composite types
   # all other combinations work also
   return ( [ how, "World" ], [ how, "PostgreSQL" ], [ how, "PL/Python" ] )
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
       </para>
      </listitem>
@@ -724,7 +576,7 @@ AS $$
       return ( self.how, self.who[self.ndx] )
 
   return producer(how, [ "World", "PostgreSQL", "PL/Python" ])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
       </para>
      </listitem>
@@ -740,7 +592,7 @@ CREATE FUNCTION greet (how text)
 AS $$
   for who in [ "World", "PostgreSQL", "PL/Python" ]:
     yield ( how, who )
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
       </para>
@@ -756,7 +608,7 @@ $$ LANGUAGE plpythonu;
 <programlisting>
 CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$
 return [(1, 2)] * n
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 SELECT * FROM multiout_simple_setof(3);
 </programlisting>
@@ -794,7 +646,7 @@ SELECT * FROM multiout_simple_setof(3);
 <programlisting>
 DO $$
     # PL/Python code
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
 
    An anonymous code block receives no arguments, and whatever value it
@@ -1089,7 +941,7 @@ CREATE FUNCTION usesavedplan() RETURNS trigger AS $$
         plan = plpy.prepare("SELECT 1")
         SD["plan"] = plan
     # rest of function
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
      </para>
     </listitem>
@@ -1132,7 +984,7 @@ for row in plpy.cursor("select num from largetable"):
     if row['num'] % 2:
          odd += 1
 return odd
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$
 odd = 0
@@ -1145,7 +997,7 @@ while True:
         if row['num'] % 2:
             odd += 1
 return odd
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$
 odd = 0
@@ -1153,7 +1005,7 @@ plan = plpy.prepare("select num from largetable where num % $1 &lt;&gt; 0", ["in
 rows = list(plpy.cursor(plan, [2]))  # or: = list(plan.cursor([2]))
 
 return len(rows)
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
      </para>
 
@@ -1198,7 +1050,7 @@ CREATE FUNCTION try_adding_joe() RETURNS text AS $$
         return "something went wrong"
     else:
         return "Joe added"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
    </para>
 
@@ -1231,7 +1083,7 @@ except plpy.SPIError as e:
     return "other error, SQLSTATE %s" % e.sqlstate
 else:
     return "fraction inserted"
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
     Note that because all exceptions from
     the <literal>plpy.spiexceptions</literal> module inherit
@@ -1280,7 +1132,7 @@ else:
     result = "funds transferred correctly"
 plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
 plpy.execute(plan, [result])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
     If the second <literal>UPDATE</literal> statement results in an
     exception being raised, this function will report the error, but
@@ -1312,7 +1164,7 @@ else:
     result = "funds transferred correctly"
 plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
 plpy.execute(plan, [result])
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 </programlisting>
     Note that the use of <literal>try/catch</literal> is still
     required.  Otherwise the exception would propagate to the top of
@@ -1329,44 +1181,6 @@ $$ LANGUAGE plpythonu;
     to be rolled back.
    </para>
   </sect2>
-
-  <sect2>
-   <title>Older Python Versions</title>
-
-   <para>
-    Context managers syntax using the <literal>with</literal> keyword
-    is available by default in Python 2.6.  For compatibility with
-    older Python versions, you can call the
-    subtransaction manager's <literal>__enter__</literal> and
-    <literal>__exit__</literal> functions using the
-    <literal>enter</literal> and <literal>exit</literal> convenience
-    aliases.  The example function that transfers funds could be
-    written as:
-<programlisting>
-CREATE FUNCTION transfer_funds_old() RETURNS void AS $$
-try:
-    subxact = plpy.subtransaction()
-    subxact.enter()
-    try:
-        plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'")
-        plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'")
-    except:
-        import sys
-        subxact.exit(*sys.exc_info())
-        raise
-    else:
-        subxact.exit(None, None, None)
-except plpy.SPIError as e:
-    result = "error transferring funds: %s" % e.args
-else:
-    result = "funds transferred correctly"
-
-plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
-plpy.execute(plan, [result])
-$$ LANGUAGE plpythonu;
-</programlisting>
-   </para>
-  </sect2>
  </sect1>
 
  <sect1 id="plpython-transactions">
@@ -1389,7 +1203,7 @@ $$ LANGUAGE plpythonu;
    Here is an example:
 <programlisting>
 CREATE PROCEDURE transaction_test1()
-LANGUAGE plpythonu
+LANGUAGE plpython3u
 AS $$
 for i in range(0, 10):
     plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i)
@@ -1465,7 +1279,7 @@ CREATE FUNCTION raise_custom_exception() RETURNS void AS $$
 plpy.error("custom exception message",
            detail="some info about exception",
            hint="hint for users")
-$$ LANGUAGE plpythonu;
+$$ LANGUAGE plpython3u;
 
 =# SELECT raise_custom_exception();
 ERROR:  plpy.Error: custom exception message
@@ -1496,6 +1310,17 @@ plpy.execute("UPDATE tbl SET %s = %s WHERE key = %s" % (
   </para>
  </sect1>
 
+ <sect1 id="plpython-python23">
+  <title>Python 2 vs. Python 3</title>
+
+  <para>
+   PL/Python supports only Python 3. Past versions of
+   <productname>PostgreSQL</productname> supported Python 2, using the
+   <literal>plpythonu</literal> and <literal>plpython2u</literal> language
+   names.
+  </para>
+ </sect1>
+
  <sect1 id="plpython-envar">
   <title>Environment Variables</title>
 
diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index b12796095fb..23d9029af9c 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -349,7 +349,7 @@ COMMENT ON TEXT SEARCH CONFIGURATION my_config IS 'Special word filtering';
 COMMENT ON TEXT SEARCH DICTIONARY swedish IS 'Snowball stemmer for Swedish language';
 COMMENT ON TEXT SEARCH PARSER my_parser IS 'Splits text into words';
 COMMENT ON TEXT SEARCH TEMPLATE snowball IS 'Snowball stemmer';
-COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'Transform between hstore and Python dict';
+COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython3u IS 'Transform between hstore and Python dict';
 COMMENT ON TRIGGER my_trigger ON my_table IS 'Used for RI';
 COMMENT ON TYPE complex IS 'Complex number data type';
 COMMENT ON VIEW my_view IS 'View of departmental costs';
diff --git a/doc/src/sgml/ref/create_transform.sgml b/doc/src/sgml/ref/create_transform.sgml
index 3f81dc6bba2..34bdc60e130 100644
--- a/doc/src/sgml/ref/create_transform.sgml
+++ b/doc/src/sgml/ref/create_transform.sgml
@@ -156,11 +156,11 @@ CREATE [ OR REPLACE ] TRANSFORM FOR <replaceable>type_name</replaceable> LANGUAG
 
   <para>
    To create a transform for type <type>hstore</type> and language
-   <literal>plpythonu</literal>, first set up the type and the language:
+   <literal>plpython3u</literal>, first set up the type and the language:
 <programlisting>
 CREATE TYPE hstore ...;
 
-CREATE EXTENSION plpythonu;
+CREATE EXTENSION plpython3u;
 </programlisting>
    Then create the necessary functions:
 <programlisting>
@@ -174,7 +174,7 @@ AS ...;
 </programlisting>
    And finally create the transform to connect them all together:
 <programlisting>
-CREATE TRANSFORM FOR hstore LANGUAGE plpythonu (
+CREATE TRANSFORM FOR hstore LANGUAGE plpython3u (
     FROM SQL WITH FUNCTION hstore_to_plpython(internal),
     TO SQL WITH FUNCTION plpython_to_hstore(internal)
 );
diff --git a/doc/src/sgml/ref/drop_transform.sgml b/doc/src/sgml/ref/drop_transform.sgml
index d25cb51604d..544a9663d77 100644
--- a/doc/src/sgml/ref/drop_transform.sgml
+++ b/doc/src/sgml/ref/drop_transform.sgml
@@ -101,9 +101,9 @@ DROP TRANSFORM [ IF EXISTS ] FOR <replaceable>type_name</replaceable> LANGUAGE <
 
   <para>
    To drop the transform for type <type>hstore</type> and language
-   <literal>plpythonu</literal>:
+   <literal>plpython3u</literal>:
 <programlisting>
-DROP TRANSFORM FOR hstore LANGUAGE plpythonu;
+DROP TRANSFORM FOR hstore LANGUAGE plpython3u;
 </programlisting></para>
  </refsect1>
 
-- 
2.35.1.354.g715d08a9e5

#77Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#76)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

A related question is whether we want to remove $(python_majorversion)
references in the makefiles?

I wouldn't. I'm doubtful of your theory that there will never be
a Python 4.

This version seems ready-to-go to me, or at least ready to see
what the buildfarm makes of it.

regards, tom lane

#78Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#77)
Re: Time to drop plpython2?

On 2022-03-07 20:11:52 -0500, Tom Lane wrote:

This version seems ready-to-go to me, or at least ready to see
what the buildfarm makes of it.

Pushed. Let's see...

#79Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#78)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

On 2022-03-07 20:11:52 -0500, Tom Lane wrote:

This version seems ready-to-go to me, or at least ready to see
what the buildfarm makes of it.

Pushed. Let's see...

wrasse says you were too quick to drop plpython_error_5.out.
Otherwise looks pretty good so far.

regards, tom lane

#80Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#79)
Re: Time to drop plpython2?

Hi,

On 2022-03-07 23:39:39 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2022-03-07 20:11:52 -0500, Tom Lane wrote:

This version seems ready-to-go to me, or at least ready to see
what the buildfarm makes of it.

Pushed. Let's see...

wrasse says you were too quick to drop plpython_error_5.out.

Does look like it. I'll try to find a distribution with an old python...

Otherwise looks pretty good so far.

crake also failed. Looks like plpy_plpymodule.h needs to include plpython.h. A
pre-existing issue that just didn't happen to cause problems...

Greetings,

Andres Freund

#81Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#80)
Re: Time to drop plpython2?

Hi,

On 2022-03-07 20:59:16 -0800, Andres Freund wrote:

On 2022-03-07 23:39:39 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:
wrasse says you were too quick to drop plpython_error_5.out.

Does look like it. I'll try to find a distribution with an old python...

debian 8 did the trick.

The output from older python was in plpython_error.out the output from newer
python in plpython_error_5.out. Looked like python2 specific output.

Pushed.

I guess I should have grokked the _5 suffix - but the _3 suffix was used for
python3 specific content, so it wasn't obvious. Should we rename the file to
plpython_error_35.out?

A bit depressing to have a 500 line alternative output file for a one line
diff :(.

Otherwise looks pretty good so far.

crake also failed. Looks like plpy_plpymodule.h needs to include plpython.h. A
pre-existing issue that just didn't happen to cause problems...

Fixed that.

Greetings,

Andres Freund

#82Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#81)
Re: Time to drop plpython2?

Andres Freund <andres@anarazel.de> writes:

A bit depressing to have a 500 line alternative output file for a one line
diff :(.

Yeah. How badly do we need that particular test case?

regards, tom lane

#83Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#82)
Re: Time to drop plpython2?

Hi,

On 2022-03-08 13:49:15 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

A bit depressing to have a 500 line alternative output file for a one line
diff :(.

Yeah. How badly do we need that particular test case?

A bit hard to tell. The test was introduced in

commit 2bd78eb8d51cc9ee03ba0287b23ff4c266dcd9b9
Author: Peter Eisentraut <peter_e@gmx.net>
Date: 2011-04-06 22:36:06 +0300

Add traceback information to PL/Python errors

This mimics the traceback information the Python interpreter prints
with exceptions.

Jan Urbański

and contains this ominous line:

+/* AttributeError at toplevel used to give segfaults with the traceback
+*/

Greetings,

Andres Freund

#84Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#81)
Re: Time to drop plpython2?

Hi,

On 2022-03-08 10:42:31 -0800, Andres Freund wrote:

crake also failed. Looks like plpy_plpymodule.h needs to include plpython.h. A
pre-existing issue that just didn't happen to cause problems...

Fixed that.

Hm. Now crake failed in XversionUpgrade-REL9_2_STABLE-HEAD:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=crake&amp;dt=2022-03-08%2018%3A47%3A22

except that the log doesn't actually indicate any problem? Andrew, any hint?

Greetings,

Andres Freund

#85Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Andres Freund (#83)
Re: Time to drop plpython2?

On 08.03.22 20:03, Andres Freund wrote:

Hi,

On 2022-03-08 13:49:15 -0500, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

A bit depressing to have a 500 line alternative output file for a one line
diff :(.

Yeah. How badly do we need that particular test case?

A bit hard to tell. The test was introduced in

commit 2bd78eb8d51cc9ee03ba0287b23ff4c266dcd9b9
Author: Peter Eisentraut <peter_e@gmx.net>
Date: 2011-04-06 22:36:06 +0300

Add traceback information to PL/Python errors

We would probably try to write this test differently today, but at this
point I wouldn't bother and just wait for Python 3.5 to fall off the end
of the conveyor belt.

#86Andrew Dunstan
andrew@dunslane.net
In reply to: Andres Freund (#84)
Re: Time to drop plpython2?

On 3/8/22 15:02, Andres Freund wrote:

Hi,

On 2022-03-08 10:42:31 -0800, Andres Freund wrote:

crake also failed. Looks like plpy_plpymodule.h needs to include plpython.h. A
pre-existing issue that just didn't happen to cause problems...

Fixed that.

Hm. Now crake failed in XversionUpgrade-REL9_2_STABLE-HEAD:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=crake&amp;dt=2022-03-08%2018%3A47%3A22

except that the log doesn't actually indicate any problem? Andrew, any hint?

That was a snafu on my part, as I was adding extension upgrade / amcheck
testing. It's fixed now, so please disregard this one.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com