From f847e4fbf7cceeafd4b1ad4a8230c06be4bfa4ef Mon Sep 17 00:00:00 2001
From: "okbob@github.com" <pavel.stehule@gmail.com>
Date: Wed, 19 Aug 2026 11:39:18 +0200
Subject: [PATCH] This patch for PostgreSQL master branch contains an
 implementation of integration Lua language to psql - PostgreSQL client.

The code is experimental - it serves to explore possibilities and necessities of this feature. But I hope it can be
useful for some users too.

The main target is a possibility to define own backslash commands in psql in Lua language.

Build
-----
```
./configure --with-lua
make all;
sudo make install
```

Features
--------

- `\luacode` - allow to enter Lua code
  ```
  \luacode
  function foo(n)
    returns n + 1;
  end;
  \.

  \luacode
  print (foo(10))
  \.
  ```
- `\luafile [FILE]` - allow to read Lua code from file

- allow to use `luapgsql`(https://github.com/SnarkyClark/luapgsql) library in Lua code
  ```
  \luacode
  local con = pg.connect('dbname=postgres host=localhost');
  local rs, err = con:exec("select * from pg_class limit 10");
  if not rs then
    print (err);
    return;
  end;
  print(string.format("rs = %s, %d rows affected", tostring(rs), rs:count()))
  local data = rs:fetch();
  while data do
    print(data.oid, data.relname);
    data = rs:fetch();
  end;
  rs:clear();
  \.
  ```
- allow to call some `psql` functionality from Lua code
  ```
  \luacode
  psql.printQuery(psql.exec("select * from pg_class limit 10"))
  \.

  \luacode
  local con = psql.connect();
  psql.printQuery(con:exec("select * from pg_class limit 10"))
  \.
  ```

- allow to execute Lua function with arguments and possibly store result to psql's variable
  multiassignment is supported
  ```
  \luacode
  function s(a, b)
    return a + b;
  end;
  \.

  \lua s 10 20
  30
  \luaset res s 10 20
  \echo :res
  30

  \luacode
  function foo(a, b, c)
    return a, b, c
  end
  \.

  \luaset a,b,c foo 10 20 30
  \echo :a :b :c
  ```

- possibility to execute string (with evaluated psql variables) as Lua expression,
  attention: psql parser quitly removes single quotes from entered string
  ```
  \set tablename footable
  \luastr psql.printQuery(psql.exec("select * from " .. :"tablename"))
  \set psqlvar ahoj
  \luastr x = :'psqlvar'
  \luastr print(x)
  ahoj
  \luastr print(:"x")
  ahoj
  ```

- possibility to define custom backslash commands
   ```
   \luacode
   psql.registerCommand( {
     name = "test",
     help_syntax = "\\test"
     help_desc = "do nothing interesting",
     handler = function(ss, ab, cmd, verbose)
       local query = "select * from pg_class";
       local opt = psq.scanSlashOptions(ss, psql.OT_NORMAL, false);
       if opt == nil then
         print "no option"
       else
         query = query .. "limit " .. opt
       end
       psql.printQuery(psql.exec(query));
       return psq.PSQL_CMD_SKIP_LINE;
     end } )
   \.
   ```
---
 configure                          | 368 +++++++++++++
 configure.ac                       | 215 ++++++++
 src/Makefile.global.in             |   4 +
 src/bin/psql/Makefile              |   7 +-
 src/bin/psql/command.c             | 559 +++++++++++++++++++-
 src/bin/psql/common.c              |   2 -
 src/bin/psql/common.h              |   5 +
 src/bin/psql/help.c                |  43 ++
 src/bin/psql/lua-psql.c            | 471 +++++++++++++++++
 src/bin/psql/lua-psql.h            |  24 +
 src/bin/psql/luapgsql.c            | 795 +++++++++++++++++++++++++++++
 src/bin/psql/luapgsql.h            |  28 +
 src/bin/psql/mainloop.c            |   1 -
 src/bin/psql/startup.c             |  46 ++
 src/bin/psql/tab-complete.in.c     | 101 +++-
 src/include/pg_config.h.in         | 232 +++++----
 src/test/regress/expected/lua.out  |  58 +++
 src/test/regress/parallel_schedule |   2 +-
 src/test/regress/sql/lua.sql       |  58 +++
 19 files changed, 2906 insertions(+), 113 deletions(-)
 create mode 100644 src/bin/psql/lua-psql.c
 create mode 100644 src/bin/psql/lua-psql.h
 create mode 100644 src/bin/psql/luapgsql.c
 create mode 100644 src/bin/psql/luapgsql.h
 create mode 100644 src/test/regress/expected/lua.out
 create mode 100644 src/test/regress/sql/lua.sql

diff --git a/configure b/configure
index d42a7a794ff..6f94dd767a4 100755
--- a/configure
+++ b/configure
@@ -631,6 +631,9 @@ PG_SYSROOT
 PG_VERSION_NUM
 LDFLAGS_EX_BE
 PROVE
+with_lua
+LUA_INCLUDES
+LUA_LIBS
 DBTOEPUB
 FOP
 XSLTPROC
@@ -888,6 +891,10 @@ with_zstd
 with_ssl
 with_openssl
 enable_largefile
+<<<<<<< Updated upstream
+=======
+with_lua
+>>>>>>> Stashed changes
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1607,6 +1614,7 @@ Optional Packages:
   --with-zstd             build with ZSTD support
   --with-ssl=LIB          use LIB for SSL/TLS support (openssl)
   --with-openssl          obsolete spelling of --with-ssl=openssl
+  --with-lua[=DIR]       use liblua (located in directory DIR, if supplied) for the lua scripting plugin.  [default=no]
 
 Some influential environment variables:
   PG_TEST_EXTRA
@@ -9247,6 +9255,12 @@ $as_echo "yes" >&6; }
 fi
 fi
 
+#
+# AC_ETHEREAL_LIBLUA_CHECK
+#
+
+
+
 #
 # XML
 #
@@ -19411,6 +19425,358 @@ $as_echo "$DBTOEPUB" >&6; }
 fi
 
 
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use liblua" >&5
+$as_echo_n "checking whether to use liblua... " >&6; }
+
+
+# Check whether --with-lua was given.
+if test "${with_lua+set}" = set; then :
+  withval=$with_lua;
+  if test $withval = no
+  then
+    with_lua=no
+  elif test $withval = yes
+  then
+    with_lua=yes
+  else
+    with_lua=yes
+    lua_dir=$withval
+  fi
+
+else
+
+  #
+  # Use liblua if it's present, otherwise don't.
+  #
+  with_lua=no
+  lua_dir=
+
+fi
+
+if test "x$with_lua" = "xno" ; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+
+  if test "x$lua_dir" != "x"
+  then
+    #
+    # The user specified a directory in which liblua resides,
+    # so add the "include" subdirectory of that directory to
+    # the include file search path and the "lib" subdirectory
+    # of that directory to the library search path.
+    #
+    # XXX - if there's also a liblua in a directory that's
+    # already in CFLAGS, CPPFLAGS, or LDFLAGS, this won't
+    # make us find the version in the specified directory,
+    # as the compiler and/or linker will search that other
+    # directory before it searches the specified directory.
+    #
+    ethereal_save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS -I$lua_dir/include"
+    ethereal_save_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="$CPPFLAGS -I$lua_dir/include"
+    ethereal_save_LIBS="$LIBS"
+    LIBS="$LIBS -L$lua_dir/lib -llua"
+    ethereal_save_LDFLAGS="$LDFLAGS"
+    LDFLAGS="$LDFLAGS -L$lua_dir/lib"
+      else
+    #
+    # The user specified no directory in which liblua resides,
+    # so just add "-llua -lliblua" to the used libs.
+    #
+    ethereal_save_CFLAGS="$CFLAGS"
+    ethereal_save_CPPFLAGS="$CPPFLAGS"
+    ethereal_save_LDFLAGS="$LDFLAGS"
+    ethereal_save_LIBS="$LIBS"
+    LIBS="$LIBS -llua"
+  fi
+
+  #
+  # Make sure we have "lua.h", "lualib.h" and "lauxlib.h".  If we don't, it means we probably
+  # don't have liblua, so don't use it.
+  #
+  for ac_header in lua.h lualib.h lauxlib.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+
+    if test "x$lua_dir" != "x"
+    then
+      #
+      # The user used "--with-lua=" to specify a directory
+      # containing liblua, but we didn't find the header file
+      # there; that either means they didn't specify the
+      # right directory or are confused about whether liblua
+      # is, in fact, installed.  Report the error and give up.
+      #
+      as_fn_error $? "liblua header not found in directory specified in --with-lua" "$LINENO" 5
+    else
+      if test "x$want_lua" = "xyes"
+      then
+        #
+        # The user tried to force us to use the library, but we
+        # couldn't find the header file; report an error.
+        #
+        as_fn_error $? "Header file lua.h not found." "$LINENO" 5
+      else
+        #
+        # We couldn't find the header file; don't use the
+        # library, as it's probably not present.
+        #
+        want_lua=no
+      fi
+    fi
+
+fi
+
+done
+
+
+  if test "x$want_lua" != "xno"
+  then
+    #
+    # Well, we at least have the lua header file.
+    #
+    # let's check if the libs are there
+    #
+
+                 # At least on Suse 9.3 systems, liblualib needs linking
+             # against libm.
+             LIBS="$LIBS $LUA_LIBS -lm"
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lua_newstate in -llua" >&5
+$as_echo_n "checking for lua_newstate in -llua... " >&6; }
+if ${ac_cv_lib_lua_lua_newstate+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-llua  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char lua_newstate ();
+int
+main ()
+{
+return lua_newstate ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_lua_lua_newstate=yes
+else
+  ac_cv_lib_lua_lua_newstate=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lua_lua_newstate" >&5
+$as_echo "$ac_cv_lib_lua_lua_newstate" >&6; }
+if test "x$ac_cv_lib_lua_lua_newstate" = xyes; then :
+
+      if test "x$lua_dir" != "x"
+      then
+        #
+        # Put the "-I" and "-L" flags for lua at
+        # the beginning of CFLAGS, CPPFLAGS,
+        # LDFLAGS, and LIBS.
+        #
+        LUA_LIBS="-L$lua_dir/lib -llua"
+        LUA_INCLUDES="-I$lua_dir/include"
+      else
+        LUA_LIBS="-llua"
+        LUA_INCLUDES=""
+      fi
+
+
+$as_echo "#define HAVE_LUA 1" >>confdefs.h
+
+
+      #
+      # we got lua, now look for lualib
+      #
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_openlib in -llualib" >&5
+$as_echo_n "checking for luaL_openlib in -llualib... " >&6; }
+if ${ac_cv_lib_lualib_luaL_openlib+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-llualib  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char luaL_openlib ();
+int
+main ()
+{
+return luaL_openlib ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_lualib_luaL_openlib=yes
+else
+  ac_cv_lib_lualib_luaL_openlib=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lualib_luaL_openlib" >&5
+$as_echo "$ac_cv_lib_lualib_luaL_openlib" >&6; }
+if test "x$ac_cv_lib_lualib_luaL_openlib" = xyes; then :
+
+        #
+        # we have 5.0
+        #
+        LUA_LIBS="$LUA_LIBS -llualib"
+
+else
+
+        #
+        # no lualib, in 5.1 there's only liblua
+        # do we have 5.1?
+        #
+
+        LIBS="$ethereal_save_LIBS $LUA_LIBS"
+
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_register in -llua" >&5
+$as_echo_n "checking for luaL_register in -llua... " >&6; }
+if ${ac_cv_lib_lua_luaL_register+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-llua  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char luaL_register ();
+int
+main ()
+{
+return luaL_register ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_lua_luaL_register=yes
+else
+  ac_cv_lib_lua_luaL_register=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lua_luaL_register" >&5
+$as_echo "$ac_cv_lib_lua_luaL_register" >&6; }
+if test "x$ac_cv_lib_lua_luaL_register" = xyes; then :
+
+            #
+            #  Lua 5.1 found
+            #
+
+$as_echo "#define HAVE_LUA_5_1 1" >>confdefs.h
+
+
+else
+
+            #
+            # No, it is not 5.1
+            #
+            if test "x$lua_dir" != "x"
+            then
+                #
+                # Restore the versions of CFLAGS, CPPFLAGS,
+                # LDFLAGS, and LIBS before we added the
+                # "--with-lua=" directory, as we didn't
+                # actually find lua there.
+                #
+                CFLAGS="$ethereal_save_CFLAGS"
+                CPPFLAGS="$ethereal_save_CPPFLAGS"
+                LDFLAGS="$ethereal_save_LDFLAGS"
+                LIBS="$ethereal_save_LIBS"
+            fi
+
+fi
+
+
+fi
+
+
+else
+
+      #
+      # Restore the versions of CFLAGS, CPPFLAGS,
+      # LDFLAGS, and LIBS before we added the
+      # "--with-lua=" directory, as we didn't
+      # actually find lua there.
+      #
+      CFLAGS="$ethereal_save_CFLAGS"
+      CPPFLAGS="$ethereal_save_CPPFLAGS"
+      LDFLAGS="$ethereal_save_LDFLAGS"
+      LIBS="$ethereal_save_LIBS"
+      LUA_LIBS=""
+      # User requested --with-lua but it isn't available
+      if test "x$want_lua" = "xyes"
+      then
+        as_fn_error $? "Linking with liblua failed." "$LINENO" 5
+      fi
+      want_lua=no
+
+fi
+
+
+  CFLAGS="$ethereal_save_CFLAGS"
+  CPPFLAGS="$ethereal_save_CPPFLAGS"
+  LDFLAGS="$ethereal_save_LDFLAGS"
+  LIBS="$ethereal_save_LIBS"
+
+
+  fi
+
+  if test "x$with_lua" = "xno" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: liblua not found - disabling support for the lua scripting plugin" >&5
+$as_echo "liblua not found - disabling support for the lua scripting plugin" >&6; }
+  fi
+fi
+
+
+
+
+
 #
 # Check for test tools
 #
@@ -21174,3 +21540,5 @@ fi
 if test "$vpath_build" = "no"; then
   touch meson.build
 fi
+
+
diff --git a/configure.ac b/configure.ac
index a331749fcb5..7d1b9dfe37e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1115,6 +1115,181 @@ if test "$with_libnuma" = yes ; then
   PKG_CHECK_MODULES(LIBNUMA, numa)
 fi
 
+#
+# AC_ETHEREAL_LIBLUA_CHECK
+#
+AC_DEFUN([AC_ETHEREAL_LIBLUA_CHECK],[
+
+  if test "x$lua_dir" != "x"
+  then
+    #
+    # The user specified a directory in which liblua resides,
+    # so add the "include" subdirectory of that directory to
+    # the include file search path and the "lib" subdirectory
+    # of that directory to the library search path.
+    #
+    # XXX - if there's also a liblua in a directory that's
+    # already in CFLAGS, CPPFLAGS, or LDFLAGS, this won't
+    # make us find the version in the specified directory,
+    # as the compiler and/or linker will search that other
+    # directory before it searches the specified directory.
+    #
+    ethereal_save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS -I$lua_dir/include"
+    ethereal_save_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="$CPPFLAGS -I$lua_dir/include"
+    ethereal_save_LIBS="$LIBS"
+    LIBS="$LIBS -L$lua_dir/lib -llua"
+    ethereal_save_LDFLAGS="$LDFLAGS"
+    LDFLAGS="$LDFLAGS -L$lua_dir/lib"
+      else
+    #
+    # The user specified no directory in which liblua resides,
+    # so just add "-llua -lliblua" to the used libs.
+    #
+    ethereal_save_CFLAGS="$CFLAGS"
+    ethereal_save_CPPFLAGS="$CPPFLAGS"
+    ethereal_save_LDFLAGS="$LDFLAGS"
+    ethereal_save_LIBS="$LIBS"
+    LIBS="$LIBS -llua"
+  fi
+
+  #
+  # Make sure we have "lua.h", "lualib.h" and "lauxlib.h".  If we don't, it means we probably
+  # don't have liblua, so don't use it.
+  #
+  AC_CHECK_HEADERS(lua.h lualib.h lauxlib.h,,
+  [
+    if test "x$lua_dir" != "x"
+    then
+      #
+      # The user used "--with-lua=" to specify a directory
+      # containing liblua, but we didn't find the header file
+      # there; that either means they didn't specify the
+      # right directory or are confused about whether liblua
+      # is, in fact, installed.  Report the error and give up.
+      #
+      AC_MSG_ERROR([liblua header not found in directory specified in --with-lua])
+    else
+      if test "x$want_lua" = "xyes"
+      then
+        #
+        # The user tried to force us to use the library, but we
+        # couldn't find the header file; report an error.
+        #
+        AC_MSG_ERROR(Header file lua.h not found.)
+      else
+        #
+        # We couldn't find the header file; don't use the
+        # library, as it's probably not present.
+        #
+        want_lua=no
+      fi
+    fi
+  ])
+
+  if test "x$want_lua" != "xno"
+  then
+    #
+    # Well, we at least have the lua header file.
+    #
+    # let's check if the libs are there
+    #
+
+                 # At least on Suse 9.3 systems, liblualib needs linking
+             # against libm.
+             LIBS="$LIBS $LUA_LIBS -lm"
+
+    AC_CHECK_LIB(lua, lua_newstate,
+    [
+      if test "x$lua_dir" != "x"
+      then
+        #
+        # Put the "-I" and "-L" flags for lua at
+        # the beginning of CFLAGS, CPPFLAGS,
+        # LDFLAGS, and LIBS.
+        #
+        LUA_LIBS="-L$lua_dir/lib -llua"
+        LUA_INCLUDES="-I$lua_dir/include"
+      else
+        LUA_LIBS="-llua"
+        LUA_INCLUDES=""
+      fi
+
+      AC_DEFINE(HAVE_LUA, 1, [Define to use Lua])
+
+      #
+      # we got lua, now look for lualib
+      #
+      AC_CHECK_LIB(lualib, luaL_openlib,
+      [
+        #
+        # we have 5.0
+        #
+        LUA_LIBS="$LUA_LIBS -llualib"
+      ],[
+        #
+        # no lualib, in 5.1 there's only liblua
+        # do we have 5.1?
+        #
+
+        LIBS="$ethereal_save_LIBS $LUA_LIBS"
+
+        AC_CHECK_LIB(lua, luaL_register,
+        [
+            #
+            #  Lua 5.1 found
+            #
+            AC_DEFINE(HAVE_LUA_5_1, 1, [Define to use Lua 5.1])
+        ],[
+            #
+            # No, it is not 5.1
+            #
+            if test "x$lua_dir" != "x"
+            then
+                #
+                # Restore the versions of CFLAGS, CPPFLAGS,
+                # LDFLAGS, and LIBS before we added the
+                # "--with-lua=" directory, as we didn't
+                # actually find lua there.
+                #
+                CFLAGS="$ethereal_save_CFLAGS"
+                CPPFLAGS="$ethereal_save_CPPFLAGS"
+                LDFLAGS="$ethereal_save_LDFLAGS"
+                LIBS="$ethereal_save_LIBS"
+            fi
+        ])
+    ])
+    ],[  
+      #
+      # Restore the versions of CFLAGS, CPPFLAGS,
+      # LDFLAGS, and LIBS before we added the
+      # "--with-lua=" directory, as we didn't
+      # actually find lua there.
+      #
+      CFLAGS="$ethereal_save_CFLAGS"
+      CPPFLAGS="$ethereal_save_CPPFLAGS"
+      LDFLAGS="$ethereal_save_LDFLAGS"
+      LIBS="$ethereal_save_LIBS"
+      LUA_LIBS=""
+      # User requested --with-lua but it isn't available
+      if test "x$want_lua" = "xyes"
+      then
+        AC_MSG_ERROR(Linking with liblua failed.)
+      fi
+      want_lua=no
+    ])
+
+  CFLAGS="$ethereal_save_CFLAGS"
+  CPPFLAGS="$ethereal_save_CPPFLAGS"
+  LDFLAGS="$ethereal_save_LDFLAGS"
+  LIBS="$ethereal_save_LIBS"
+  AC_SUBST(LUA_LIBS)
+  AC_SUBST(LUA_INCLUDES)
+  fi
+])
+
+
 #
 # XML
 #
@@ -2475,6 +2650,44 @@ PGAC_PATH_PROGS(XSLTPROC, xsltproc)
 PGAC_PATH_PROGS(FOP, fop)
 PGAC_PATH_PROGS(DBTOEPUB, dbtoepub)
 
+
+dnl lua check
+AC_MSG_CHECKING(whether to use liblua)
+
+AC_ARG_WITH(lua,
+[  --with-lua[[=DIR]]       use liblua (located in directory DIR, if supplied) for the lua scripting plugin.  [[default=no]]],
+[
+  if test $withval = no
+  then
+    with_lua=no
+  elif test $withval = yes
+  then
+    with_lua=yes
+  else
+    with_lua=yes
+    lua_dir=$withval
+  fi
+],[
+  #
+  # Use liblua if it's present, otherwise don't.
+  #
+  with_lua=no
+  lua_dir=
+])
+if test "x$with_lua" = "xno" ; then
+  AC_MSG_RESULT(no)
+else
+  AC_MSG_RESULT(yes)
+  AC_ETHEREAL_LIBLUA_CHECK
+  if test "x$with_lua" = "xno" ; then
+    AC_MSG_RESULT(liblua not found - disabling support for the lua scripting plugin)
+  fi
+fi
+
+AC_SUBST(with_lua)
+
+
+
 #
 # Check for test tools
 #
@@ -2635,3 +2848,5 @@ AC_OUTPUT
 if test "$vpath_build" = "no"; then
   touch meson.build
 fi
+
+
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index cef1ad7f87d..614bdd615d1 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -204,6 +204,7 @@ with_llvm	= @with_llvm@
 with_system_tzdata = @with_system_tzdata@
 with_uuid	= @with_uuid@
 with_zlib	= @with_zlib@
+with_lua	= @with_lua@
 enable_rpath	= @enable_rpath@
 enable_nls	= @enable_nls@
 enable_debug	= @enable_debug@
@@ -552,6 +553,9 @@ GENHTML = @GENHTML@
 DEF_PGPORT = @default_port@
 WANTED_LANGUAGES = @WANTED_LANGUAGES@
 
+# Lua support
+LUA_INCLUDES = @LUA_INCLUDES@
+LUA_LIBS = @LUA_LIBS@
 
 ##########################################################################
 #
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index be0032652cd..9c94b6945e5 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -41,8 +41,13 @@ OBJS = \
 	startup.o \
 	stringutils.o \
 	tab-complete.o \
-	variables.o
+	variables.o \
 
+ifdef with_lua
+OBJS += luapgsql.o lua-psql.o
+CFLAGS += $(LUA_INCLUDES) -DLUA_SUPPORT
+LDFLAGS_INTERNAL += $(LUA_LIBS)
+endif
 
 all: psql
 
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 1008a46f048..8c6cff3f777 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -38,12 +38,22 @@
 #include "input.h"
 #include "large_obj.h"
 #include "libpq/pqcomm.h"
+
+#ifdef LUA_SUPPORT
+
+#include "lua-psql.h"
+
+#endif
+
 #include "mainloop.h"
 #include "pqexpbuffer.h"
 #include "psqlscanslash.h"
+#include "prompt.h"
 #include "settings.h"
 #include "variables.h"
 
+#include "fe_utils/simple_list.h"
+
 /*
  * Editable database object types.
  */
@@ -69,6 +79,16 @@ static backslashResult exec_command_cd(PsqlScanState scan_state, bool active_bra
 									   const char *cmd);
 static backslashResult exec_command_close_prepared(PsqlScanState scan_state,
 												   bool active_branch, const char *cmd);
+
+#ifdef LUA_SUPPORT
+
+static backslashResult exec_command_luastr(PsqlScanState scan_state, bool active_branch);
+static backslashResult exec_command_luacode(PsqlScanState scan_state, bool active_branch);
+static backslashResult exec_command_luafile(PsqlScanState scan_state, bool active_branch);
+static backslashResult exec_command_lua(PsqlScanState scan_state, bool active_branch, const char *cmd);
+
+#endif
+
 static backslashResult exec_command_conninfo(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_copy(PsqlScanState scan_state, bool active_branch);
 static backslashResult exec_command_copyright(PsqlScanState scan_state, bool active_branch);
@@ -199,7 +219,6 @@ static void checkWin32Codepage(void);
 static bool restricted;
 static char *restrict_key;
 
-
 /*----------
  * HandleSlashCmds:
  *
@@ -351,6 +370,21 @@ exec_command(const char *cmd,
 		status = exec_command_cd(scan_state, active_branch, cmd);
 	else if (strcmp(cmd, "close_prepared") == 0)
 		status = exec_command_close_prepared(scan_state, active_branch, cmd);
+
+#ifdef LUA_SUPPORT
+
+	else if (strcmp(cmd, "luastr") == 0)
+		status = exec_command_luastr(scan_state, active_branch);
+	else if (strcmp(cmd, "luacode") == 0)
+		status = exec_command_luacode(scan_state, active_branch);
+	else if (strcmp(cmd, "luafile") == 0)
+		status = exec_command_luafile(scan_state, active_branch);
+	else if ((strcmp(cmd, "lua") == 0) ||
+			 (strcmp(cmd, "luaset") == 0))
+		status = exec_command_lua(scan_state, active_branch, cmd);
+
+#endif
+
 	else if (strcmp(cmd, "conninfo") == 0)
 		status = exec_command_conninfo(scan_state, active_branch);
 	else if (pg_strcasecmp(cmd, "copy") == 0)
@@ -479,8 +513,13 @@ exec_command(const char *cmd,
 		status = exec_command_shell_escape(scan_state, active_branch);
 	else if (strcmp(cmd, "?") == 0)
 		status = exec_command_slash_command_help(scan_state, active_branch);
-	else
-		status = PSQL_CMD_UNKNOWN;
+
+#ifdef LUA_SUPPORT
+
+	else 
+		status = exec_lua_command(lua, scan_state, active_branch, cmd);
+
+#endif
 
 	/*
 	 * All the commands that return PSQL_CMD_SEND want to execute previous_buf
@@ -783,6 +822,520 @@ exec_command_close_prepared(PsqlScanState scan_state, bool active_branch, const
 	return status;
 }
 
+/*
+ * reads one line from multiline string.
+ * Doesn't modify origin string, doesn't remove EOLN marker
+ */
+static char *
+getline_binary(char **str, int *bytes)
+{
+	char	   *eoln;
+	char	   *result;
+
+	if (!*str)
+		return NULL;
+
+	result = *str;
+	eoln = strchr(result, '\n');
+
+	if (eoln)
+	{
+		*str = eoln + 1;
+		*bytes = *str - result;
+	}
+	else
+	{
+		*str = NULL;
+		*bytes = strlen(result);
+	}
+
+	return result;
+}
+
+static bool
+is_EOF_marker(char *str, int bytes)
+{
+	if ((bytes == 2 && memcmp(str, "\\.", 2) == 0) ||
+		(bytes == 3 && memcmp(str, "\\.\n", 3) == 0) ||
+		(bytes == 4 && memcmp(str, "\\.\r\n", 7) == 0))
+	{
+		return true;
+	}
+
+	return false;
+}
+
+#ifdef LUA_SUPPORT
+
+static backslashResult
+exec_command_luastr(PsqlScanState scan_state, bool active_branch)
+{
+	PQExpBufferData expr;
+	char	   *opt;
+	int			result;
+
+	if (!active_branch && pset.cur_cmd_interactive)
+	{
+		ignore_slash_whole_line(scan_state);
+		return PSQL_CMD_SKIP_LINE;
+	}
+
+	initPQExpBuffer(&expr);
+
+	/*
+	 * Attention: psql_scan_slash_option quitly eats single quotes
+	 */
+	opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
+
+	while (opt)
+	{
+		appendPQExpBuffer(&expr, "%s ", opt);
+		free(opt);
+		opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
+	}
+
+	result = luaL_dostring(lua, expr.data);
+	if (result != LUA_OK)
+	{
+		pg_log_error("Error: %s", lua_tostring(lua, -1));
+
+		termPQExpBuffer(&expr);
+		/* pop error message */
+		lua_pop(lua, 1);
+
+		return PSQL_CMD_ERROR;
+	}
+
+	termPQExpBuffer(&expr);
+
+	return PSQL_CMD_SKIP_LINE;
+}
+
+/*
+ * \luacode - inputs code until \.
+ *
+ * Note: when luacode is executed from history or from temp file, then
+ * OT_WHOLE_LINE contains complete multiline string - unfortunately
+ * trimmed from both sides, so we cannot to place some optional arguments to
+ * first line. And when last row is not EOF marker we must add new line.
+ */
+static backslashResult
+exec_command_luacode(PsqlScanState scan_state, bool active_branch)
+{
+#define READCODE_BUFSIZE		1024
+
+	bool		showprompt = false;
+	bool		readcode_done = false;
+	PQExpBufferData		code;
+	char	   *opt;
+	int			result;
+
+	if (!active_branch && pset.cur_cmd_interactive)
+	{
+		ignore_slash_whole_line(scan_state);
+		return PSQL_CMD_SKIP_LINE;
+	}
+
+	showprompt = pset.cur_cmd_interactive && !pset.quiet;
+
+	initPQExpBuffer(&code);
+
+	/* the content can be passed as multiline string in cmd line */
+	opt = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, false);
+
+	if (opt)
+	{
+		char	   *lines = opt;
+
+		while (lines)
+		{
+			char	   *line;
+			int			bytes;
+
+			line = getline_binary(&lines, &bytes);
+
+			if (!is_EOF_marker(line, bytes))
+			{
+				appendBinaryPQExpBuffer(&code, line, bytes);
+				if (!lines)
+					appendPQExpBufferChar(&code, '\n');
+			}
+			else
+				readcode_done = true;
+		}
+
+		free(opt);
+	}
+
+	if (!readcode_done)
+	{
+		bool		at_line_begin = true;
+		char		buf[READCODE_BUFSIZE];
+
+		/*
+		 * EOF flag can be set on pset.cur_cmd_source from previous execution
+		 */
+		clearerr(pset.cur_cmd_source);
+
+		showprompt = pset.cur_cmd_interactive && !pset.quiet;
+
+		/*
+		 * Establish longjmp destination for exiting from wait-for-input. (This is
+		 * only effective while sigint_interrupt_enabled is TRUE.)
+		 */
+		if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
+			goto code_cleanup;
+
+		if (showprompt)
+			puts(_("Enter code to be copied followed by a newline.\n"
+				   "End with a backslash and a period on a line by itself, or an EOF signal."));
+
+		while (!readcode_done)
+		{
+			char	   *fgresult;
+
+			if (at_line_begin && showprompt)
+			{
+				const char *prompt = get_prompt(PROMPT_COPY, NULL);
+
+				fputs(prompt, stdout);
+				fflush(stdout);
+			}
+
+			/* enable longjmp while waiting for input */
+			sigint_interrupt_enabled = true;
+
+			fgresult = fgets(buf, READCODE_BUFSIZE, pset.cur_cmd_source);
+
+			sigint_interrupt_enabled = false;
+
+			if (!fgresult)
+			{
+				readcode_done = true;
+				if (showprompt)
+				{
+					fputs("\\.\n", stdout);
+					fflush(stdout);
+				}
+			}
+			else
+			{
+				int			linelen;
+
+				linelen = strlen(fgresult);
+
+				if (buf[linelen - 1] == '\n')
+				{
+					if (at_line_begin)
+					{
+						if (is_EOF_marker(buf, linelen))
+							readcode_done = true;
+					}
+
+					pset.lineno++;
+					pset.stmt_lineno++;
+
+					at_line_begin = true;
+				}
+				else
+					at_line_begin = false;
+
+				if (!readcode_done)
+					appendBinaryPQExpBuffer(&code, buf, linelen);
+			}
+		}
+
+		if (ferror(pset.cur_cmd_source))
+			goto code_cleanup;
+	}
+
+	if (!active_branch)
+		return PSQL_CMD_SKIP_LINE;
+
+	result = luaL_dostring(lua, code.data);
+	if (result != LUA_OK)
+	{
+		pg_log_error("Error: %s", lua_tostring(lua, -1));
+
+		/* pop error message */
+		lua_pop(lua, 1);
+		termPQExpBuffer(&code);
+
+		return PSQL_CMD_ERROR;
+	}
+
+	fflush(stdout);
+
+	return PSQL_CMD_SKIP_LINE;
+
+code_cleanup:
+
+	if (showprompt)
+	{
+		fputs("\n", stdout);
+		fflush(stdout);
+	}
+
+	termPQExpBuffer(&code);
+
+	return PSQL_CMD_ERROR;
+
+}
+
+/*
+ * \luafile - read lua code from file
+ */
+static backslashResult
+exec_command_luafile(PsqlScanState scan_state, bool active_branch)
+{
+	char	   *opt;
+	int			result;
+
+	if (!active_branch && pset.cur_cmd_interactive)
+	{
+		ignore_slash_whole_line(scan_state);
+		return PSQL_CMD_SKIP_LINE;
+	}
+
+	opt = psql_scan_slash_option(scan_state,
+								 OT_NORMAL, NULL, true);
+
+	if (!opt)
+	{
+		pg_log_error("\\luafile: missing required argument");
+		return PSQL_CMD_ERROR;
+	}
+
+	expand_tilde(&opt);
+
+	result = luaL_dofile(lua, opt);
+
+	free(opt);
+
+	if (result != LUA_OK)
+	{
+		pg_log_error("Error: %s", lua_tostring(lua, -1));
+
+		/* pop error message */
+		lua_pop(lua, 1);
+
+		return PSQL_CMD_ERROR;
+	}
+
+	return PSQL_CMD_SKIP_LINE;
+}
+
+/*
+ * lua luafunction arguments - run lua function with arguments
+ */
+static backslashResult
+exec_command_lua(PsqlScanState scan_state, bool active_branch, const char *cmd)
+{
+	char	   *opt = NULL;
+	int			result;
+	int			nargs = 0;
+	int			stacksize;
+	int			nresults;
+	char	   *varname = NULL;
+	SimpleStringList vars = {NULL, NULL};
+	bool		isLuaSet = false;
+
+	if (!active_branch)
+		return PSQL_CMD_SKIP_LINE;
+
+	if (strcmp(cmd, "luaset") == 0)
+	{
+		isLuaSet = true;
+
+		for (;;)
+		{
+			/*
+			 * Parsing list of variables is complex. Main reason is fact
+			 * so slash option parser uses only space as token delimiter,
+			 * and one slash option can contain one or more variables
+			 * separated by comma, can contains only comma, comma can be on
+			 * the start or on the end - all possible variants should be
+			 * supported
+			 *
+			 * a,b,c
+			 * a , b , c
+			 * a, b, c
+			 * a ,b, c
+			 */
+			if (!varname)
+				varname = psql_scan_slash_option(scan_state,
+												 OT_NORMAL, NULL, false);
+
+			if (!varname)
+			{
+				pg_log_error("\\%s: missing required argument", cmd);
+				return PSQL_CMD_ERROR;
+			}
+
+			if (varname[0] != '\'' && varname[0] != '"')
+			{
+				char	   *names = varname;
+				bool		last_token_is_comma = false;
+
+				/* slash option can hold multiple varnames - x,y */
+				while (*names)
+				{
+					char	   *ptr;
+
+					if (*names == ',')
+					{
+						pg_log_error("\\%s: missing required argument before \",\"", cmd);
+						free(varname);
+						return PSQL_CMD_ERROR;
+					}
+
+					ptr = strchr(names, ',');
+					if (ptr)
+						*ptr = '\0';
+
+					simple_string_list_append(&vars, names);
+
+					if (ptr)
+					{
+						last_token_is_comma = true;
+						names = ptr + 1;
+					}
+					else
+					{
+						last_token_is_comma = false;
+						break;
+					}
+				}
+
+				if (last_token_is_comma)
+				{
+					free(varname);
+					varname = NULL;
+					continue;
+				}
+			}
+			else
+			{
+				simple_string_list_append(&vars, varname);
+				free(varname);
+			}
+
+			varname = psql_scan_slash_option(scan_state,
+											 OT_NORMAL, NULL, false);
+			if (!varname)
+				break;
+			else if (strcmp(varname, ",") == 0)
+			{
+				free(varname);
+				varname = NULL;
+				continue;
+			}
+			else if (varname[0] == ',')
+			{
+				char	   *aux = varname;
+
+				varname = strdup(varname + 1);
+				free(aux);
+				continue;
+			}
+
+			opt = varname;
+			break;
+		}
+	}
+	else
+	{
+		opt = psql_scan_slash_option(scan_state,
+									 OT_NORMAL, NULL, false);
+	}
+
+	if (!opt)
+	{
+		pg_log_error("\\%s: missing required argument", cmd);
+		simple_string_list_destroy(&vars);
+		return PSQL_CMD_ERROR;
+	}
+
+	stacksize = lua_gettop(lua);
+
+	lua_getglobal(lua, opt);
+	if (!lua_isfunction(lua, -1)) {
+		fprintf(stderr, "ERROR: %s is not function!\n", opt);
+		lua_settop(lua, stacksize);
+		simple_string_list_destroy(&vars);
+		return PSQL_CMD_ERROR;
+	}
+
+	free(opt);
+	opt = psql_scan_slash_option(scan_state,
+								 OT_NORMAL, NULL, true);
+
+	while (opt)
+	{
+		lua_pushstring(lua, opt);
+		free(opt);
+		opt = psql_scan_slash_option(scan_state,
+									 OT_NORMAL, NULL, true);
+		nargs++;
+	}
+
+	result = lua_pcall(lua, nargs, LUA_MULTRET, 0);
+
+	if (result != LUA_OK) {
+		fprintf(stderr, "Error running lua: %s\n", lua_tostring(lua, -1));
+		lua_settop(lua, stacksize);
+		simple_string_list_destroy(&vars);
+		return PSQL_CMD_ERROR;
+	}
+
+	nresults = lua_gettop(lua) - stacksize;
+
+	if (isLuaSet)
+	{
+		for (SimpleStringListCell *cell = vars.head; cell; cell = cell->next)
+		{
+			varname = cell->val;
+
+			if (nresults > 0)
+			{
+				if (lua_isnil(lua, - nresults))
+					SetVariable(pset.vars, varname, NULL);
+				else
+					SetVariable(pset.vars, varname, lua_tostring(lua, - nresults));
+
+				nresults--;
+			}
+			else
+				SetVariable(pset.vars, varname, NULL);
+		}
+	}
+	else
+	{
+		bool		isfirst = true;
+
+		while (nresults)
+		{
+			if (!isfirst)
+				fprintf(stderr, "\t");
+			else
+				isfirst = false;
+
+			fprintf(stderr, "%s", lua_tostring(lua, - nresults));
+			nresults--;
+		}
+
+		if (!isfirst)
+			fprintf(stderr, "\n");
+	}
+
+	lua_settop(lua, stacksize);
+	simple_string_list_destroy(&vars);
+
+	return PSQL_CMD_SKIP_LINE;
+}
+
+#endif
+
 /*
  * \conninfo -- display information about the current connection
  */
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index f220344daaf..c6a44e2b7b9 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -642,7 +642,6 @@ PrintTiming(double elapsed_msec)
 		   elapsed_msec, days, (int) hours, (int) minutes, seconds);
 }
 
-
 /*
  * PSQLexec
  *
@@ -795,7 +794,6 @@ PrintQueryTuples(const PGresult *result, const printQueryOpt *opt,
 	return ok;
 }
 
-
 /*
  * StoreQueryTuple: assuming query result is OK, save data into variables
  *
diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h
index d4b99b42331..9ecb39e38d7 100644
--- a/src/bin/psql/common.h
+++ b/src/bin/psql/common.h
@@ -15,6 +15,9 @@
 #include "fe_utils/psqlscan.h"
 #include "libpq-fe.h"
 
+#include <lualib.h>
+#include <lauxlib.h>
+
 extern bool openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe);
 extern bool setQFout(const char *fname);
 
@@ -46,4 +49,6 @@ extern void clean_extended_state(void);
 
 extern bool recognized_connection_string(const char *connstr);
 
+extern lua_State *lua;
+
 #endif							/* COMMON_H */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index b3aa2217fee..9283ffa8811 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -25,6 +25,10 @@
 #include "input.h"
 #include "settings.h"
 #include "sql_help.h"
+#include "fe_utils/simple_list.h"
+#include "fe_utils/mbprint.h"
+#include "common.h"
+#include "lua-psql.h"
 
 /*
  * PLEASE:
@@ -151,6 +155,7 @@ slashUsage(unsigned short int pager)
 	int			nlcount;
 	FILE	   *output;
 	char	   *currdb;
+	SimplePtrList help_strings = {NULL, NULL};
 
 	currdb = PQdb(pset.db);
 
@@ -327,6 +332,15 @@ slashUsage(unsigned short int pager)
 	HELP0("  \\unset NAME            unset (delete) internal variable\n");
 	HELP0("\n");
 
+	HELP0("Lua\n");
+	HELP0("  \\lua FUNCNAME [args]   execute lua function with arguments\n");
+	HELP0("  \\luacode               reads lua source code\n");
+	HELP0("  \\luafile [FILE]        reads lua source code from file\n");
+	HELP0("  \\luaset VARNAME FUNCNAME [args]\n"
+		  "                         set result of Lua function to variable\n");
+	HELP0("  \\luastr LUAEXPR        execute evaluated string as Lua code\n");
+	HELP0("\n");
+
 	HELP0("Extended Query Protocol\n");
 	HELP0("  \\bind [PARAM]...       set query parameters\n");
 	HELP0("  \\bind_named STMT_NAME [PARAM]...\n"
@@ -342,6 +356,35 @@ slashUsage(unsigned short int pager)
 	HELP0("  \\startpipeline         enter pipeline mode\n");
 	HELP0("  \\syncpipeline          add a synchronisation point to an ongoing pipeline\n");
 
+	if (lua_help_strings(lua, &help_strings) > 0)
+	{
+		SimplePtrListCell *cell;
+		HelpStruct *hlp;
+
+		HELP0("\nCustom commands\n");
+
+		for (cell = help_strings.head; cell; cell = cell->next)
+		{
+			int syntax_width;
+
+			hlp = (HelpStruct *) cell->ptr;
+			syntax_width = pg_wcswidth(hlp->syntax, strlen(hlp->syntax), pset.encoding);
+
+			if (syntax_width > 22)
+			{
+				HELPN("  %s\n                         %s\n", hlp->syntax, hlp->desc);
+			}
+			else
+			{
+				HELPN("  %s%*s %s\n", hlp->syntax, 22- syntax_width, " ", hlp->desc);
+			}
+
+			free(cell->ptr);
+		}
+
+		simple_ptr_list_destroy(&help_strings);
+	}
+
 	/* Now we can count the lines. */
 	nlcount = 0;
 	for (const char *ptr = buf.data; *ptr; ptr++)
diff --git a/src/bin/psql/lua-psql.c b/src/bin/psql/lua-psql.c
new file mode 100644
index 00000000000..ea9b86126bd
--- /dev/null
+++ b/src/bin/psql/lua-psql.c
@@ -0,0 +1,471 @@
+#include "lua-psql.h"
+
+#include "postgres_fe.h"
+#include "command.h"
+#include "common.h"
+#include "settings.h"
+#include "common/logging.h"
+#include "psqlscanslash.h"
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "luapgsql.h"
+
+LUALIB_API int L_psql_exec(lua_State *L);
+LUALIB_API int L_psql_printQuery(lua_State *L);
+LUALIB_API int L_psql_connect(lua_State *L);
+LUALIB_API int L_psql_scan_slash_option(lua_State *L);
+LUALIB_API int L_psql_downcase_identifier(lua_State *L);
+LUALIB_API int L_psql_registerCommand(lua_State *L);
+LUALIB_API int L_psql_log_error(lua_State *L);
+
+#define TYPE_PSQL_SCANSTATE "psql.ScanState"
+
+typedef struct PsqlScanState_t
+{
+	PsqlScanState ptr;
+} PsqlScanState_t;
+
+#define lua_to_psql_ScanState(L, i) ((PsqlScanState_t *)(lua_touserdata(L, i)))
+#define lua_new_psql_ScanState(L) ((PsqlScanState_t *)(lua_newuserdata(L, sizeof(PsqlScanState_t))))
+
+/* open the library - used by require() */
+LUALIB_API int
+luaopen_psql(lua_State *L)
+{
+	luaL_Reg luapsql[] = {
+		{"exec", L_psql_exec},
+		{"connect", L_psql_connect},
+		{"printQuery", L_psql_printQuery},
+		{"scanSlashOption", L_psql_scan_slash_option},
+		{"downcaseIdentifier", L_psql_downcase_identifier},
+		{"registerCommand", L_psql_registerCommand},
+		{"logError", L_psql_log_error},
+		{NULL, NULL}
+	};
+
+	struct
+	{
+		char	   *name;
+		int			value;
+	} psql_enums[] = {
+		{"OT_NORMAL", OT_NORMAL},
+		{"OT_SQLID", OT_SQLID},
+		{"OT_SQLIDHACK", OT_SQLIDHACK},
+		{"OT_FILEPIPE", OT_FILEPIPE},
+		{"OT_WHOLE_LINE", OT_WHOLE_LINE},
+		{"PSQL_CMD_SEND", PSQL_CMD_SEND},
+		{"PSQL_CMD_SKIP_LINE", PSQL_CMD_SKIP_LINE},
+		{"PSQL_CMD_TERMINATE", PSQL_CMD_TERMINATE},
+		{"PSQL_CMD_NEWEDIT", PSQL_CMD_NEWEDIT},
+		{"PSQL_CMD_ERROR", PSQL_CMD_ERROR},
+		{ NULL, 0 }
+	};
+
+	int			i;
+
+	luaL_newlib(L, luapsql);
+
+	i = 0;
+	while (psql_enums[i].name)
+	{
+		lua_pushstring(L, psql_enums[i].name);
+		lua_pushnumber(L, (double) psql_enums[i].value);
+		lua_settable(L, -3);
+		i++;
+	}
+
+	lua_pushliteral(L, "_handlers");
+	lua_newtable(L);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "_help_strings");
+	lua_newtable(L);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "_tab_complete_handlers");
+	lua_newtable(L);
+	lua_settable(L, -3);
+
+	luaL_newmetatable(L, TYPE_PSQL_SCANSTATE);
+	lua_pop(L, 1);
+
+	return 1;
+}
+
+/*
+ * psql.printQuery
+ */
+LUALIB_API int
+L_psql_printQuery(lua_State *L)
+{
+	rs_t *rs = lua_check_pgresult(L, 1);
+	if (PQresultStatus(rs->ptr) == PGRES_TUPLES_OK)
+	{
+		printQuery(rs->ptr, &pset.popt, pset.queryFout, false, pset.logfile);
+		fflush(pset.queryFout);
+		if (ferror(pset.queryFout))
+		{
+			pg_log_error("could not print result table: %m");
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * psql.exec(query)
+ */
+LUALIB_API int
+L_psql_exec(lua_State *L)
+{
+	const char *query = (const char*)luaL_checkstring(L, 1);
+	PGresult *rs;
+
+	rs = PSQLexec(query);
+
+	if (rs)
+	{
+		ExecStatusType status = PQresultStatus(rs);
+		if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) {
+			lua_push_pgresult(L, rs);
+			lua_pushnil(L);
+		} else {
+			lua_pushnil(L);
+			lua_pushstring(L, PQresultErrorMessage(rs));
+			PQclear(rs);
+		}
+	}
+	else
+	{
+		lua_pushnil(L);
+		lua_pushliteral(L, "FATAL error");
+	}
+
+	return 2;
+}
+
+/*
+ * psql.connect()
+ */
+LUALIB_API int
+L_psql_connect(lua_State *L)
+{
+	if (!pset.db)
+	{
+		pg_log_error("You are currently not connected to a database.");
+		lua_pushnil(L);
+	}
+
+	lua_push_pgconn(L, pset.db, true);
+
+	return 1;
+}
+
+static PsqlScanState_t *
+lua_check_psql_ScanState(lua_State *L, int i)
+{
+	luaL_checkudata(L, i, TYPE_PSQL_SCANSTATE);
+	return lua_to_psql_ScanState(L, i);
+}
+
+static void
+lua_push_ScanState(lua_State *L, PsqlScanState state)
+{
+	PsqlScanState_t *p = lua_new_psql_ScanState(L);
+
+	luaL_getmetatable(L, TYPE_PSQL_SCANSTATE);
+	lua_setmetatable(L, -2);
+	p->ptr = state;
+}
+
+LUALIB_API int
+L_psql_scan_slash_option(lua_State *L)
+{
+	PsqlScanState_t *p = lua_check_psql_ScanState(L, 1);
+	double ot = lua_tonumber(L, 2);
+	bool semicolon = lua_toboolean(L, 3);
+
+	char	   *result;
+	char	   quote;
+
+	result = psql_scan_slash_option(p->ptr, (int) ot, &quote, semicolon);
+
+	if (result)
+	{
+		lua_pushstring(L, result);
+		lua_pushfstring(L, "%c", quote);
+		free(result);
+	}
+	else
+	{
+		lua_pushnil(L);
+		lua_pushnil(L);
+	}
+
+	return 2;
+}
+
+LUALIB_API int
+L_psql_downcase_identifier(lua_State *L)
+{
+	const char	   *str = lua_tostring(L, 1);
+	bool		downcase = lua_toboolean(L, 2);
+
+	char	   *aux = strdup(str);
+
+	dequote_downcase_identifier(aux, downcase, pset.encoding);
+
+	lua_pushstring(L, aux);
+	free(aux);
+
+	return 1;
+}
+
+LUALIB_API int
+L_psql_registerCommand(lua_State *L)
+{
+	const char   *name;
+
+	const char   *help_string_syntax = NULL;
+	const char   *help_string_desc = NULL;
+
+	lua_pushliteral(L, "help_syntax");
+	lua_gettable(L, 1);
+	if (lua_isstring(L, -1))
+	{
+		help_string_syntax = lua_tostring(L, -1);
+	}
+
+	lua_pushliteral(L, "help_desc");
+	lua_gettable(L, 1);
+	if (lua_isstring(L, -1))
+	{
+		help_string_desc = lua_tostring(L, -1);
+	}
+
+	if (!help_string_syntax && help_string_desc)
+	{
+		lua_pushliteral(L, "incorrect argument, command description without command syntax");
+		lua_error(L);
+	}
+
+	lua_getglobal(L, "psql");
+	lua_getfield(L, -1, "_handlers");
+
+	if (!lua_istable(L, 1))
+	{
+		lua_pushliteral(L, "incorrect argument");
+		lua_error(L);
+	}
+
+	lua_pushliteral(L, "name");
+	lua_gettable(L, 1);
+	if (!lua_isstring(L, -1))
+	{
+		lua_pushliteral(L, "incorrect argument, missing \"name\"");
+		lua_error(L);
+	}
+
+	name = lua_tostring(L, -1);
+
+	lua_pushliteral(L, "handler");
+	lua_gettable(L, 1);
+
+	if (lua_isnil(L, -1))
+	{
+		lua_pushliteral(L, "incorrect argument, missing \"handler\"");
+		lua_error(L);
+	}
+
+	if (!lua_isfunction(L, -1))
+	{
+		lua_pushliteral(L, "incorrect argument, handler is not a function");
+		lua_error(L);
+	}
+
+	lua_settable(L, -3);
+
+	if (help_string_syntax)
+	{
+		lua_getglobal(L, "psql");
+		lua_getfield(L, -1, "_help_strings");
+		lua_pushstring(L, help_string_syntax);
+
+		if (help_string_desc)
+		{
+			lua_pushstring(L, help_string_desc);
+		}
+		else
+			lua_pushnil(L);
+
+		lua_settable(L, -3);
+	}
+
+	/*
+	 * tab complete handler is optional
+	 */
+	lua_getglobal(L, "psql");
+	lua_getfield(L, -1, "_tab_complete_handlers");
+
+	lua_pushstring(L, name);
+
+	lua_pushliteral(L, "tab_complete_handler");
+	lua_gettable(L, 1);
+	if (!lua_isnil(L, -1))
+	{
+		if (!lua_isfunction(L, -1))
+		{
+			lua_pushliteral(L, "incorrect argument, tab complete handler is not a function");
+			lua_error(L);
+		}
+
+		lua_settable(L, -3);
+	}
+
+	return 0;
+}
+
+LUALIB_API int
+L_psql_log_error(lua_State *L)
+{
+	const char	   *str = lua_tostring(L, 1);
+
+	pg_log_error("%s", str);
+
+	return 0;
+}
+
+int
+exec_lua_command(lua_State *L,
+				 PsqlScanState scan_state,
+				 bool active_branch,
+				 const char *cmd)
+{
+	int			result;
+	char	   *buffer;
+	bool		verbose;
+
+	buffer = strdup(cmd);
+
+	if (buffer[strlen(buffer) - 1] == '+')
+	{
+		verbose = true;
+		buffer[strlen(buffer) - 1] = '\0';
+	}
+	else
+		verbose = false;
+
+	lua_getglobal(L, "psql");
+	lua_getfield(L, -1, "_handlers");
+	if (lua_isnil(L, -1))
+	{
+		pg_log_error("table psql._handlers is not available");
+		free(buffer);
+		return PSQL_CMD_ERROR;
+	}
+
+	lua_pushstring(L, buffer);
+	lua_gettable(L, -2);
+	if (lua_isnil(L, -1))
+	{
+		lua_pop(L, 1);
+		free(buffer);
+		return PSQL_CMD_UNKNOWN;
+	}
+
+	if (!lua_isfunction(L, -1))
+	{
+		pg_log_error("handler is not Lua function");
+		lua_pop(L, 1);
+		free(buffer);
+		return PSQL_CMD_ERROR;
+	}
+
+	lua_push_ScanState(L, scan_state);
+	lua_pushboolean(L, active_branch);
+	lua_pushstring(L, cmd);
+
+	lua_pushboolean(L, verbose);
+
+	result = lua_pcall(L, 4, 1, 0);
+
+	if (result != LUA_OK)
+	{
+		pg_log_error("Error running lua: %s", lua_tostring(L, -1));
+		lua_pop(L, 1);
+		free(buffer);
+		return PSQL_CMD_ERROR;
+	}
+
+	result = (int) lua_tonumber(L, -1);
+	lua_pop(L, 1);
+	free(buffer);
+
+	return result;
+}
+
+int
+lua_custom_commands(lua_State *L, SimpleStringList *commands)
+{
+	char		buffer[1024];
+	int			nfields = 0;
+
+	lua_getglobal(L, "psql");
+	lua_getfield(L, -1, "_handlers");
+
+	lua_pushnil(L);
+	while (lua_next(L, -2) != 0)
+	{
+		snprintf(buffer, sizeof(buffer), "\\%s", lua_tostring(L, -2));
+		simple_string_list_append(commands, buffer);
+		nfields++;
+		lua_pop(L, 1);
+	}
+
+	return nfields;
+}
+
+int
+lua_global_functions(lua_State *L, SimpleStringList *functions)
+{
+	int			n = 0;
+
+	lua_pushglobaltable(L);
+	lua_pushnil(L);
+	while (lua_next(L, -2) != 0)
+	{
+		if (lua_isfunction(L, -1))
+		{
+			simple_string_list_append(functions, lua_tostring(L, -2));
+			n++;
+		}
+		lua_pop(L, 1);
+	}
+	lua_pop(L, 1);
+	return n;
+}
+
+int
+lua_help_strings(lua_State *L, SimplePtrList *help_strings)
+{
+	int			nfields = 0;
+
+	lua_getglobal(L, "psql");
+	lua_getfield(L, -1, "_help_strings");
+
+	lua_pushnil(L);
+	while (lua_next(L, -2) != 0)
+	{
+		HelpStruct *hlp = pg_malloc(sizeof(HelpStruct));
+
+		hlp->syntax = lua_tostring(L, -2);
+		hlp->desc = lua_tostring(L, -1);
+		simple_ptr_list_append(help_strings, hlp);
+		nfields++;
+		lua_pop(L, 1);
+	}
+
+	return nfields;
+}
diff --git a/src/bin/psql/lua-psql.h b/src/bin/psql/lua-psql.h
new file mode 100644
index 00000000000..8bced4f093a
--- /dev/null
+++ b/src/bin/psql/lua-psql.h
@@ -0,0 +1,24 @@
+#ifndef LUA_PSQL_H
+#define LUA_PSQL_H
+
+#include "postgres_fe.h"
+#include "fe_utils/simple_list.h"
+#include <lua.h>
+
+typedef struct
+{
+	const char	   *syntax;
+	const char	   *desc;
+} HelpStruct;
+
+typedef struct PsqlScanStateData *PsqlScanState;
+
+LUALIB_API int luaopen_psql(lua_State *L);
+
+extern int exec_lua_command(lua_State *L, PsqlScanState scan_state, bool active_branch, const char *cmd);
+
+extern int lua_custom_commands(lua_State *L, SimpleStringList *commands);
+extern int lua_global_functions(lua_State *L, SimpleStringList *functions);
+extern int lua_help_strings(lua_State *L, SimplePtrList *help_strings);
+
+#endif
\ No newline at end of file
diff --git a/src/bin/psql/luapgsql.c b/src/bin/psql/luapgsql.c
new file mode 100644
index 00000000000..f2c917e097d
--- /dev/null
+++ b/src/bin/psql/luapgsql.c
@@ -0,0 +1,795 @@
+#include <stdlib.h>
+#include <sys/select.h>
+#include <lua.h>
+#include <lauxlib.h>
+#include <libpq-fe.h>
+
+#include "luapgsql.h"
+
+#include "postgres_fe.h"
+
+
+
+#define MYNAME "pgsql"
+#define MYVERSION MYNAME " library for " LUA_VERSION " 1.0.1"
+
+#define TYPE_CONNECTION "PgSQL.Connection"
+#define TYPE_RESULT "PgSQL.Result"
+
+#define lua_boxpointer(L, u) (*(void **)(lua_newuserdata(L, sizeof(void *))) = (u))
+#define lua_unboxpointer(L, i) (*(void **)(lua_touserdata(L, i)))
+
+#define lua_newconn(L) ((con_t *)(lua_newuserdata(L, sizeof(con_t))))
+#define lua_toconn(L, i) ((con_t *)(lua_touserdata(L, i)))
+
+#define lua_newresult(L) ((rs_t *)(lua_newuserdata(L, sizeof(rs_t))))
+#define lua_toresult(L, i) ((rs_t *)(lua_touserdata(L, i)))
+
+#define BOOLOID         16
+#define INT8OID         20
+#define INT2OID         21
+#define INT4OID         23
+#define FLOAT4OID		700
+#define FLOAT8OID		701
+#define NUMERICOID      1700
+
+/** module registration **/
+
+/* pg.connect - connect to a database */
+LUALIB_API int L_connect(lua_State *L);
+
+/** PgSQL.Connection object **/
+
+/* con:escape - escape a string for use in queries */
+LUALIB_API int L_con_escape(lua_State *L);
+/* con:exec - execute a sql command, with or without parameters */
+LUALIB_API int L_con_exec(lua_State *L);
+/* con:notifywait - wait for any NOTIFY message from server */
+LUALIB_API int L_con_notifywait(lua_State *L);
+/* con:close - close the connection and free the client resources */
+LUALIB_API int L_con_close(lua_State *L);
+/* con:clone - opens new connection with same properties */
+LUALIB_API int L_con_clone(lua_State *L);
+
+/* connection object garbage collector */
+LUALIB_API int L_con_gc(lua_State *L);
+
+
+/** PgSQL.Result object **/
+
+/* rs:count - the number of rows returned OR affected by the sql command */
+LUALIB_API int L_res_count(lua_State *L);
+/* rs:fetch - traditional 'fetch' interface */
+LUALIB_API int L_res_fetch(lua_State *L);
+/* rs:cols generator */
+LUALIB_API int L_res_cols(lua_State *L);
+/* rs:cols iterator */
+LUALIB_API int L_res_col_iter (lua_State *L);
+/* rs:rows generator */
+LUALIB_API int L_res_rows(lua_State *L);
+/* rs:rows iterator */
+LUALIB_API int L_res_row_iter (lua_State *L);
+/* rs:clear - free the result set */
+LUALIB_API int L_res_clear(lua_State *L);
+/* result object garbage collector */
+LUALIB_API int L_res_gc(lua_State *L);
+
+/* con:exec - execute a sql command without waiting for the result, with or without parameters */
+LUALIB_API int L_con_send_query(lua_State *L);
+
+LUALIB_API int L_con_is_busy(lua_State *L);
+LUALIB_API int L_con_consume_input(lua_State *L);
+
+LUALIB_API int L_con_get_result(lua_State *L);
+LUALIB_API int L_con_resultwait(lua_State *L);
+
+static PGconn *copy_connection(lua_State *L, PGconn *conn);
+
+
+/** Lua 5.1 compatibility **/
+
+#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501
+/*
+** Adapted from Lua 5.2.0
+*/
+static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
+	luaL_checkstack(L, nup+1, "too many upvalues");
+	for (; l->name != NULL; l++) {  /* fill the table with given functions */
+		int i;
+		lua_pushstring(L, l->name);
+		for (i = 0; i < nup; i++)  /* copy upvalues to the top */
+			lua_pushvalue(L, -(nup+1));
+		lua_pushcclosure(L, l->func, nup);  /* closure with those upvalues */
+		lua_settable(L, -(nup + 3));
+	}
+	lua_pop(L, nup);  /* remove upvalues */
+}
+#else
+	#define luaL_getn luaL_len
+#endif
+
+/** private helper functions **/
+
+/* server NOTICE message handler */
+static void pg_notice(void *arg, const char *message) {
+#ifdef DEBUG
+	fprintf(stderr, "debug.notice [%s]\n", message);
+#endif
+}
+
+/* check and return pointer */
+static void *luaL_checkpointer(lua_State* L, int i) {
+	luaL_checktype(L, i, LUA_TLIGHTUSERDATA);
+	return lua_touserdata(L, i);
+}
+
+/* check and return connection object parameter */
+con_t *lua_check_pgconn(lua_State* L, int i) {
+	luaL_checkudata(L, i, TYPE_CONNECTION);
+	return lua_toconn(L, i);
+}
+
+/* check and return result object parameter */
+rs_t *lua_check_pgresult(lua_State* L, int i) {
+	luaL_checkudata(L, i, TYPE_RESULT);
+	return lua_toresult(L, i);
+}
+
+/* push a connection object on the stack */
+void lua_push_pgconn(lua_State *L, PGconn *con, bool shared) {
+	con_t *p = lua_newconn(L);
+	luaL_getmetatable(L, TYPE_CONNECTION);
+	lua_setmetatable(L, -2);
+	p->ptr = con;
+	p->open = 1;
+	p->shared = shared;
+#ifdef DEBUG
+	fprintf(stderr, "debug.lua_pushconn ptr [%p]\n", (void *)p->ptr);
+	fprintf(stderr, "debug.lua_pushconn open [%d]\n", p->open);
+#endif
+}
+
+/* push a result object on the stack */
+void lua_push_pgresult(lua_State *L, PGresult *rs) {
+	rs_t *p = lua_newresult(L);
+	luaL_getmetatable(L, TYPE_RESULT);
+	lua_setmetatable(L, -2);
+	p->ptr = rs;
+	p->open = 1;
+	p->row = 0;
+#ifdef DEBUG
+	fprintf(stderr, "debug.lua_pushresult ptr [%p]\n", (void *)p->ptr);
+	fprintf(stderr, "debug.lua_pushresult open [%d]\n", p->open);
+	fprintf(stderr, "debug.lua_pushresult row [%d]\n", p->row);
+#endif
+}
+
+/* get one value from PGresult and push it onto the Lua stack */
+static void lua_pushpgdata(lua_State *L, PGresult *rs, int row, int col) {
+	const char *val;
+	double temp;
+	/* grab the value */
+	if (PQgetisnull(rs, row, col)) {
+		/* tasty NULLs - take that PHP! */
+		lua_pushnil(L);
+	} else {
+		val = PQgetvalue(rs, row, col);
+		switch (PQftype(rs, col)) {
+		case BOOLOID:
+			/* map postgresql default bool format */
+			if (val[0] == 't') {
+				lua_pushboolean(L, 1);
+			} else {
+				lua_pushboolean(L, 0);
+			}
+			break;
+		case INT2OID:
+		case INT4OID:
+		case INT8OID:
+		case FLOAT4OID:
+		case FLOAT8OID:
+			/* convert using Lua string -> number conversion for reliability */
+			lua_pushstring(L, val);
+			temp = lua_tonumber(L, -1);
+			lua_pop(L, 1);
+			lua_pushnumber(L, temp);
+			break;
+		case NUMERICOID:
+		default:
+			/* it's all just a string after that */
+			lua_pushstring(L, val);
+			break;
+		}
+	}
+}
+
+/* push a table onto the stack containing a row of data from PGresult - by edo1 */
+static void lua_pushpgrow(lua_State *L, PGresult *rs, int row) {
+	int col;
+	int cols = PQnfields(rs);
+	lua_createtable(L, cols, cols);
+	for (col = 0; col < cols; col++) {
+		/* grab the data */
+		lua_pushpgdata(L, rs, row, col);
+		/* give us an indexed ... */
+		lua_pushvalue(L, -1);
+		lua_rawseti(L, -3, col + 1);
+		/* ... and assoc array */
+		lua_pushstring(L, PQfname(rs, col));
+		lua_pushvalue(L, -2);
+		lua_settable(L, -4);
+		lua_pop(L, 1);
+	}
+}
+
+/** module registration **/
+
+/* base functions */
+static const luaL_Reg R_pg_functions[] = {
+	{"connect", L_connect},
+	{NULL, NULL}
+};
+
+/* connection objects methods */
+static const luaL_Reg R_con_methods[] = {
+	{"escape", L_con_escape},
+	{"exec", L_con_exec},
+	{"sendquery", L_con_send_query},
+	{"isbusy", L_con_is_busy},
+	{"consumeinput", L_con_consume_input},
+	{"notifywait", L_con_notifywait},
+	{"close", L_con_close},
+	{"clone", L_con_clone},
+	{"getresult", L_con_get_result},
+	{"resultwait", L_con_resultwait},
+	{NULL, NULL}
+};
+
+/* result object methods */
+static const luaL_Reg R_res_methods[] = {
+	{"count", L_res_count},
+	{"fetch", L_res_fetch},
+	{"cols", L_res_cols},
+	{"rows", L_res_rows},
+	{"clear", L_res_clear},
+	{NULL, NULL}
+};
+
+/* open the library - used by require() */
+LUALIB_API int luaopen_pgsql(lua_State *L) {
+	/* register the base functions and module tags */
+	lua_newtable(L);
+	luaL_setfuncs(L, R_pg_functions, 0);
+	lua_pushliteral(L,"version");			/** version */
+	lua_pushliteral(L,MYVERSION);
+	lua_settable(L,-3);
+	/* register the connection object type */
+	luaL_newmetatable(L, TYPE_CONNECTION);
+	lua_pushvalue(L, -1);
+	lua_setfield(L, -2, "__index");
+	luaL_setfuncs(L, R_con_methods, 0);
+	lua_pushcfunction(L, L_con_gc);
+	lua_setfield(L, -2, "__gc");
+	lua_pop(L, 1);
+	/* register the result object type */
+	luaL_newmetatable(L, TYPE_RESULT);
+	lua_pushvalue(L, -1);
+	lua_setfield(L, -2, "__index");
+	luaL_setfuncs(L, R_res_methods, 0);
+	lua_pushcfunction(L, L_res_gc);
+	lua_setfield(L, -2, "__gc");
+	lua_pop(L, 1);
+	/* return the library handle */
+	return 1;
+}
+
+
+/** exported functions **/
+
+/* pg.connect - connect to a database */
+LUALIB_API int L_connect(lua_State *L) {
+	PGconn *con = NULL;
+	const char *info = (const char*)luaL_checkstring(L, 1);
+	con = PQconnectdb(info);
+	if (PQstatus(con) == CONNECTION_OK) {
+		PQsetNoticeProcessor(con, pg_notice, (void *)con);
+		lua_push_pgconn(L, con, false);
+		lua_pushnil(L);
+	} else {
+		lua_pushnil(L);
+		lua_pushstring(L, PQerrorMessage(con));
+		PQfinish(con);
+	}
+	return 2;
+}
+
+/** PgSQL.Connection object **/
+
+/* con:escape - escape a string for use in queries */
+LUALIB_API int L_con_escape(lua_State *L) {
+	size_t len;
+	const char *src; char *dst;
+	/* con_t *con = luaL_checkconn(L, 1); */
+	src = luaL_checklstring(L, 2, &len);
+	dst = calloc(len * 2 + 1, sizeof(char));
+	/* PQescapeStringConn(con->ptr, dst, src, len, NULL); */
+	PQescapeString(dst, src, len);
+	lua_pushstring(L, dst);
+	free(dst);
+	return 1;
+}
+
+/* con:exec - execute a sql command, with or without parameters */
+LUALIB_API int L_con_exec(lua_State *L) {
+	PGresult *rs = NULL;
+	const char **param = NULL;
+	int param_count;
+	char *bool_t[2] = {"FALSE", "TRUE"};
+	con_t *con = lua_check_pgconn(L, 1);
+	const char *sql = luaL_checkstring(L, 2);
+	int i;
+#ifdef DEBUG
+	fprintf(stderr, "debug.lua_con_exec ptr [%p]\n", (void *)con->ptr);
+	fprintf(stderr, "debug.lua_con_exec open [%d]\n", con->open);
+	fprintf(stderr, "debug.lua_con_exec sql [%s]\n", sql);
+#endif
+	if (PQstatus(con->ptr) == CONNECTION_OK) {
+		if (lua_gettop(L) == 2) {
+			/* no parameters, just an 'ol fashioned query */
+			rs = PQexec(con->ptr, sql);
+		} else {
+			/* parameterized query */
+			luaL_checktype(L, 3, LUA_TTABLE);
+			if (lua_gettop(L) >= 4) {
+				/* parameter count given, use it */
+				param_count = luaL_checkinteger(L, 4);
+			} else {
+				/* parameter count not given, trust in the force (luaL_getn) */
+				param_count = luaL_getn(L, 3);
+			}
+			/* clear-allocate params for PQexecParams */
+			if (param_count > 0) param = calloc(param_count, sizeof(char *));
+			/* load params from Lua table into C array */
+			for (i = 0; i < param_count; i++) {
+				lua_rawgeti(L, 3, i + 1);
+				if (lua_type(L, -1) == LUA_TBOOLEAN) {
+					/* convert boolean into "TRUE" or "FALSE" */
+					param[i] = bool_t[lua_toboolean(L, -1)];
+				} else {
+					param[i] = lua_tostring(L, -1);
+				}
+			}
+			rs = PQexecParams(con->ptr, sql, param_count, NULL, param, NULL, NULL, 0);
+			if (param) {
+				lua_pop(L, param_count);	
+				free(param);
+			}
+		}
+		if (rs) {
+			ExecStatusType status = PQresultStatus(rs);
+			if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) {
+				lua_push_pgresult(L, rs);
+				lua_pushnil(L);
+			} else {
+				lua_pushnil(L);
+				lua_pushstring(L, PQresultErrorMessage(rs));
+				PQclear(rs);
+			}
+		} else {
+			lua_pushnil(L);
+			lua_pushliteral(L, "FATAL error");
+		}
+	} else {
+		lua_pushnil(L);
+		lua_pushliteral(L, "Connection Failure");
+	}
+	return 2;
+}
+
+/* con:exec - execute a sql command, with or without parameters */
+LUALIB_API int L_con_get_result(lua_State *L) {
+	PGresult *rs = NULL;
+	con_t *con = lua_check_pgconn(L, 1);
+#ifdef DEBUG
+	fprintf(stderr, "debug.lua_con_exec ptr [%p]\n", (void *)con->ptr);
+	fprintf(stderr, "debug.lua_con_exec open [%d]\n", con->open);
+#endif
+	if (PQstatus(con->ptr) == CONNECTION_OK) {
+		rs = PQgetResult(con->ptr);
+
+		if (rs) {
+			ExecStatusType status = PQresultStatus(rs);
+			if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) {
+				lua_push_pgresult(L, rs);
+				lua_pushnil(L);
+			} else {
+				lua_pushnil(L);
+				lua_pushstring(L, PQresultErrorMessage(rs));
+				PQclear(rs);
+			}
+		} else {
+			lua_pushnil(L);
+			lua_pushliteral(L, "FATAL error");
+		}
+	} else {
+		lua_pushnil(L);
+		lua_pushliteral(L, "Connection Failure");
+	}
+	return 2;
+}
+
+
+/* con:exec - execute a sql command without waiting for the result, with or without parameters */
+LUALIB_API int L_con_send_query(lua_State *L) {
+	int			result;
+	const char **param = NULL;
+	int param_count;
+	char *bool_t[2] = {"FALSE", "TRUE"};
+	con_t *con = lua_check_pgconn(L, 1);
+	const char *sql = luaL_checkstring(L, 2);
+	int i;
+#ifdef DEBUG
+	fprintf(stderr, "debug.lua_con_exec ptr [%p]\n", (void *)con->ptr);
+	fprintf(stderr, "debug.lua_con_exec open [%d]\n", con->open);
+	fprintf(stderr, "debug.lua_con_exec sql [%s]\n", sql);
+#endif
+	if (PQstatus(con->ptr) == CONNECTION_OK) {
+		if (lua_gettop(L) == 2) {
+			/* no parameters, just an 'ol fashioned query */
+			result = PQsendQuery(con->ptr, sql);
+		} else {
+			/* parameterized query */
+			luaL_checktype(L, 3, LUA_TTABLE);
+			if (lua_gettop(L) >= 4) {
+				/* parameter count given, use it */
+				param_count = luaL_checkinteger(L, 4);
+			} else {
+				/* parameter count not given, trust in the force (luaL_getn) */
+				param_count = luaL_getn(L, 3);
+			}
+			/* clear-allocate params for PQexecParams */
+			if (param_count > 0) param = calloc(param_count, sizeof(char *));
+			/* load params from Lua table into C array */
+			for (i = 0; i < param_count; i++) {
+				lua_rawgeti(L, 3, i + 1);
+				if (lua_type(L, -1) == LUA_TBOOLEAN) {
+					/* convert boolean into "TRUE" or "FALSE" */
+					param[i] = bool_t[lua_toboolean(L, -1)];
+				} else {
+					param[i] = lua_tostring(L, -1);
+				}
+			}
+			result = PQsendQueryParams(con->ptr, sql, param_count, NULL, param, NULL, NULL, 0);
+			if (param) {
+				lua_pop(L, param_count);
+				free(param);
+			}
+		}
+		if (result) {
+			lua_pushboolean(L, true);
+			lua_pushnil(L);
+		} else {
+			lua_pushnil(L);;
+			lua_pushstring(L, PQerrorMessage(con->ptr));
+		}
+	} else {
+		lua_pushnil(L);
+		lua_pushliteral(L, "Connection Failure");
+	}
+	return 0;
+}
+
+LUALIB_API int L_con_is_busy(lua_State *L) {
+	con_t *con = lua_check_pgconn(L, 1);
+	if (PQstatus(con->ptr) == CONNECTION_OK) {
+		lua_pushboolean(L, PQisBusy(con->ptr) == 1 ? true : false);
+		lua_pushnil(L);
+	} else {
+		lua_pushnil(L);
+		lua_pushliteral(L, "Connection Failure");
+	}
+	return 2;
+}
+
+LUALIB_API int L_con_consume_input(lua_State *L) {
+	con_t *con = lua_check_pgconn(L, 1);
+	if (PQstatus(con->ptr) == CONNECTION_OK) {
+		int result = PQconsumeInput(con->ptr);
+		if (!result) {
+			lua_pushnil(L);
+			lua_pushstring(L, PQerrorMessage(con->ptr));
+		}
+		else
+		{
+			lua_pushboolean(L, true);
+			lua_pushnil(L);
+		}
+	} else {
+		lua_pushnil(L);
+		lua_pushliteral(L, "Connection Failure");
+	}
+	return 2;
+}
+
+/* con:notifywait - wait for any NOTIFY message from server - by edo1 */
+LUALIB_API int L_con_notifywait(lua_State *L) {
+	con_t *con = lua_check_pgconn(L, 1);
+	int sock;
+	fd_set input_mask;
+	struct timeval tv;
+	struct timeval *tvp;
+	PGnotify *notify;
+	int nnotifies = 0;
+	if (lua_gettop(L) >= 2) {
+		lua_Number t = lua_tonumber(L,2);
+		tv.tv_sec = t;
+		tv.tv_usec = (t - tv.tv_sec) * 1000000;
+		tvp = &tv;
+	} else {
+		tvp = NULL;
+	}
+	sock = PQsocket(con->ptr);
+	/* Now check for input */
+	do {
+		PQconsumeInput(con->ptr);
+		while ((notify = PQnotifies(con->ptr)) != NULL)
+		{
+			PQfreemem(notify);
+			nnotifies++;
+		}
+		if (nnotifies > 0) {
+			tv.tv_sec = 0;
+			tv.tv_usec = 0;
+			tvp = &tv;
+		}
+		FD_ZERO(&input_mask);
+		FD_SET(sock, &input_mask);
+	} while (select(sock + 1, &input_mask, NULL, NULL, tvp) > 0);
+	lua_pushinteger(L, nnotifies);
+	return 1;
+}
+
+/* con:notifywait - wait for any NOTIFY message from server - by edo1 */
+LUALIB_API int L_con_resultwait(lua_State *L) {
+	con_t *con = lua_check_pgconn(L, 1);
+	int sock;
+	int result;
+	fd_set input_mask;
+	struct timeval tv;
+	struct timeval *tvp;
+	if (lua_gettop(L) >= 2) {
+		lua_Number t = lua_tonumber(L,2);
+		tv.tv_sec = t;
+		tv.tv_usec = (t - tv.tv_sec) * 1000000;
+		tvp = &tv;
+	} else {
+		tvp = NULL;
+	}
+	PQconsumeInput(con->ptr);
+	sock = PQsocket(con->ptr);
+	/* Now check for input */
+	FD_ZERO(&input_mask);
+	FD_SET(sock, &input_mask);
+	result = select(sock + 1, &input_mask, NULL, NULL, tvp);
+	lua_pushinteger(L, result);
+	return 1;
+}
+
+
+/* con:close - close the connection and free the client resources */
+LUALIB_API int L_con_close(lua_State *L) {
+	con_t *con = lua_check_pgconn(L, 1);
+	if (con->open && !con->shared) {
+		con->open = false;
+		PQfinish(con->ptr);
+	}
+	return 0;
+}
+
+/* con:close - close the connection and free the client resources */
+LUALIB_API int L_con_clone(lua_State *L) {
+	PGconn *newpgconn;
+	con_t *newcon;
+	con_t *con = lua_check_pgconn(L, 1);
+
+	if (con->open) {
+		newpgconn = copy_connection(L, con->ptr);
+		newcon = lua_newconn(L);
+		luaL_getmetatable(L, TYPE_CONNECTION);
+		lua_setmetatable(L, -2);
+
+		newcon->ptr = newpgconn;
+		newcon->open = true;
+		newcon->shared = false;
+	}
+	else
+	{
+		lua_pushliteral(L, "cannot to clone closed connection");
+		lua_error(L);
+	}
+	return 1;
+}
+
+
+/* connection object garbage collector */
+LUALIB_API int L_con_gc(lua_State *L) {
+	if (lua_isuserdata(L, 1)) {
+		con_t *con = lua_toconn(L, 1);
+#ifdef DEBUG
+		fprintf(stderr, "debug.lua_con_gc ptr [%p]\n", (void *)con->ptr);
+		fprintf(stderr, "debug.lua_con_gc open [%d]\n", con->open);
+#endif
+		if (con->open && !con->shared) {
+			con->open = false;
+			PQfinish(con->ptr);
+		}
+	}
+	return 0;
+}
+
+
+/** PgSQL.Result object **/
+
+/* rs:count - the number of rows returned OR affected by the sql command */
+LUALIB_API int L_res_count(lua_State *L) {
+	int n;
+	rs_t *rs = lua_check_pgresult(L, 1);
+	if (PQresultStatus(rs->ptr) == PGRES_TUPLES_OK) {
+		lua_pushinteger(L, PQntuples(rs->ptr));
+	} else if (PQresultStatus(rs->ptr) == PGRES_COMMAND_OK) {
+		lua_pushstring(L, PQcmdTuples(rs->ptr));
+		n = lua_tonumber(L, -1);
+		lua_pop(L, 1);
+		lua_pushinteger(L, n);
+	}
+	return 1;
+}
+
+/* rs:fetch - tranditional 'fetch' interface */
+LUALIB_API int L_res_fetch(lua_State *L) {
+	rs_t *rs = lua_check_pgresult(L, 1);
+	int rows = PQntuples(rs->ptr);
+	if (rs->row < rows) {
+		lua_pushpgrow(L, rs->ptr, rs->row);
+		/* next row */
+		rs->row++;
+		return 1;
+	} else {
+		/* no more values to return */
+		return 0;
+	}
+}
+
+/* rs:cols generator */
+LUALIB_API int L_res_cols(lua_State *L) {
+	rs_t *rs = lua_check_pgresult(L, 1);
+	lua_pushlightuserdata(L, rs->ptr);
+	/* start at column 0 */
+	lua_pushinteger(L, 0);
+	lua_pushcclosure(L, L_res_col_iter, 2);
+	return 1;
+}
+
+/* rs:cols iterator */
+LUALIB_API int L_res_col_iter (lua_State *L) {
+	int cols; int col;
+	PGresult *rs;
+	rs = (PGresult *)luaL_checkpointer(L, lua_upvalueindex(1));
+	/* current column */
+	col = luaL_checkinteger(L, lua_upvalueindex(2));
+	/* number of columns */
+	cols = PQnfields(rs);
+	if (col < cols) {
+		lua_pushinteger(L, col + 1);
+		lua_pushstring(L, PQfname(rs, col));
+		lua_pushinteger(L, col + 1); /* next column */
+		lua_replace(L, lua_upvalueindex(2));
+		return 2;
+	} else return 0;  /* no more values to return */
+}
+
+/* rs:rows generator */
+LUALIB_API int L_res_rows(lua_State *L) {
+	rs_t *rs = lua_check_pgresult(L, 1);
+	lua_pushlightuserdata(L, rs->ptr);
+	/* start at row 0 */
+	lua_pushinteger(L, 0);
+	lua_pushcclosure(L, L_res_row_iter, 2);
+	return 1;
+}
+
+/* rs:rows iterator */
+LUALIB_API int L_res_row_iter (lua_State *L) {
+	int row;
+	int rows;
+	PGresult *rs;
+	rs = (PGresult *)luaL_checkpointer(L, lua_upvalueindex(1));
+	/* current row */
+	row = luaL_checkinteger(L, lua_upvalueindex(2));
+	rows = PQntuples(rs);
+	if (row < rows) {
+		lua_pushpgrow(L, rs, row);
+		/* next row */
+		lua_pushinteger(L, row + 1);
+		lua_replace(L, lua_upvalueindex(2));
+		return 1;
+	} else {
+        /* no more values to return */
+		return 0;
+	}
+}
+
+/* rs:clear - free the result set */
+LUALIB_API int L_res_clear(lua_State *L) {
+	rs_t *rs = lua_check_pgresult(L, 1);
+	if (rs->open) {
+		rs->open = false;
+		PQclear(rs->ptr);
+	}
+	return 0;
+}
+
+/* result object garbage collector */
+LUALIB_API int L_res_gc(lua_State *L) {
+	if (lua_isuserdata(L, 1)) {
+		rs_t *rs = lua_check_pgresult(L, 1);
+#ifdef DEBUG
+		fprintf(stderr, "debug.lua_rs_gc ptr [%p]\n", (void *)rs->ptr);
+		fprintf(stderr, "debug.lua_rs_gc open [%d]\n", rs->open);
+#endif
+		if (rs->open) {
+			rs->open = false;
+			PQclear(rs->ptr);
+		}
+	}
+	return 0;
+}
+
+/*
+ * Create a new connection with the same conninfo as the given one.
+ */
+static PGconn *
+copy_connection(lua_State *L, PGconn *conn)
+{
+	PGconn	   *copyConn;
+	PQconninfoOption *opts = PQconninfo(conn);
+	const char **keywords;
+	const char **vals;
+	int			nopts = 0;
+	int			i;
+
+	for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
+		nopts++;
+	nopts++;					/* for the NULL terminator */
+
+	keywords = pg_malloc_array(const char *, nopts);
+	vals = pg_malloc_array(const char *, nopts);
+
+	i = 0;
+	for (PQconninfoOption *opt = opts; opt->keyword != NULL; ++opt)
+	{
+		if (opt->val)
+		{
+			keywords[i] = opt->keyword;
+			vals[i] = opt->val;
+			i++;
+		}
+	}
+	keywords[i] = vals[i] = NULL;
+
+	copyConn = PQconnectdbParams(keywords, vals, false);
+
+	pg_free(keywords);
+	pg_free(vals);
+	PQconninfoFree(opts);
+
+	if (PQstatus(copyConn) != CONNECTION_OK)
+	{
+		lua_pushnil(L);
+		lua_pushstring(L, PQerrorMessage(copyConn));
+		PQfinish(copyConn);
+	}
+
+	return copyConn;
+}
+
diff --git a/src/bin/psql/luapgsql.h b/src/bin/psql/luapgsql.h
new file mode 100644
index 00000000000..cb6dbc25b62
--- /dev/null
+++ b/src/bin/psql/luapgsql.h
@@ -0,0 +1,28 @@
+#ifndef LUAPGSQL_H
+#define LUAPGSQL_H
+
+#include <lua.h>
+#include <libpq-fe.h>
+
+typedef struct con_t {
+	PGconn *ptr;
+	bool shared;
+	bool open;
+} con_t;
+
+typedef struct rs_t {
+	PGresult *ptr;
+	bool open;
+	int row;
+} rs_t;
+
+extern con_t *lua_check_pgconn(lua_State *L, int i);
+extern rs_t *lua_check_pgresult(lua_State *L, int i);
+extern void lua_push_pgconn(lua_State *L, PGconn *con, bool shared);
+extern void lua_push_pgresult(lua_State *L, PGresult *rs);
+
+/** module registration **/
+
+LUALIB_API int luaopen_pgsql(lua_State *L);
+
+#endif
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index c3f85a609bc..5392de2fcba 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -21,7 +21,6 @@ const PsqlScanCallbacks psqlscan_callbacks = {
 	psql_get_variable,
 };
 
-
 /*
  * Main processing loop for reading lines of input
  *	and sending them to the backend.
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index bb33c7c14d7..01e955a221d 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -27,6 +27,15 @@
 #include "portability/instr_time.h"
 #include "settings.h"
 
+#ifdef LUA_SUPPORT
+
+#include <lualib.h>
+#include <lauxlib.h>
+#include "luapgsql.h"
+#include "lua-psql.h"
+
+#endif
+
 /*
  * Global psql options
  */
@@ -87,6 +96,12 @@ static void process_psqlrc_file(char *filename);
 static void showVersion(void);
 static void EstablishVariableSpace(void);
 
+#ifdef LUA_SUPPORT
+
+lua_State	   *lua = NULL;
+
+#endif
+
 #define NOPAGER		0
 
 static void
@@ -211,6 +226,31 @@ main(int argc, char *argv[])
 	SetVariable(pset.vars, "PIPELINE_COMMAND_COUNT", "0");
 	SetVariable(pset.vars, "PIPELINE_RESULT_COUNT", "0");
 
+#ifdef LUA_SUPPORT
+
+
+	lua = luaL_newstate();
+	luaopen_base(lua);
+	luaopen_string(lua);
+	lua_setglobal(lua, "string");
+
+	luaopen_table(lua);
+	lua_setglobal(lua, "table");
+
+	luaopen_pgsql(lua);
+	lua_setglobal(lua, "pg");
+
+	luaopen_psql(lua);
+	lua_setglobal(lua, "psql");
+
+	luaopen_io(lua);
+	lua_setglobal(lua, "io");
+
+	/* Initialize lua support */
+	SetVariable(pset.vars, "LUA_RELEASE", LUA_RELEASE);
+
+#endif
+
 	parse_psql_options(argc, argv, &options);
 
 	/*
@@ -471,6 +511,12 @@ error:
 		successResult = MainLoop(stdin);
 	}
 
+#ifdef LUA_SUPPORT
+
+	lua_close(lua);
+
+#endif
+
 	/* clean up */
 	if (pset.logfile)
 		fclose(pset.logfile);
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 1b74fa62c5c..8efffabf493 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -60,6 +60,12 @@
 #include "settings.h"
 #include "stringutils.h"
 
+#ifdef LUA_SUPPORT
+
+#include "lua-psql.h"
+
+#endif
+
 /*
  * Ancient versions of libedit provide filename_completion_function()
  * instead of rl_filename_completion_function().  Likewise for
@@ -1919,6 +1925,13 @@ psql_completion(const char *text, int start, int end)
 	static const char *const backslash_commands[] = {
 		"\\a",
 		"\\bind", "\\bind_named",
+
+#ifdef LUA_SUPPORT
+
+		"\\luacode", "\\luafile", "\\luaset", "\\luastr",
+
+#endif
+
 		"\\connect", "\\conninfo", "\\C", "\\cd", "\\close_prepared", "\\copy",
 		"\\copyright", "\\crosstabview",
 		"\\d", "\\da", "\\dA", "\\dAc", "\\dAf", "\\dAo", "\\dAp",
@@ -1983,8 +1996,58 @@ psql_completion(const char *text, int start, int end)
 
 	/* If current word is a backslash command, offer completions for that */
 	if (text[0] == '\\')
+	{
+
+#ifdef LUA_SUPPORT
+
+		int		lua_commands = 0;
+
+		SimpleStringList custom_commands = {NULL, NULL};
+
+		lua_commands = lua_custom_commands(lua, &custom_commands);
+		if (lua_commands > 0)
+		{
+			int		i;
+			const char **commands;
+			SimpleStringListCell *cell;
+
+			i = 0;
+			while (backslash_commands[i])
+				i++;
+
+			commands = pg_malloc((i + lua_commands + 1) * sizeof(char *));
+
+			i = 0;
+			while (backslash_commands[i])
+			{
+				commands[i] = backslash_commands[i];
+				i++;
+			}
+
+			for (cell = custom_commands.head; cell; cell = cell->next)
+			{
+				commands[i] = cell->val;
+				i++;
+			}
+
+			commands[i] = NULL;
+
+			COMPLETE_WITH_LIST_CS(commands);
+
+			simple_string_list_destroy(&custom_commands);
+			free(commands);
+		}
+		else
+			COMPLETE_WITH_LIST_CS(backslash_commands);
+
+#else
+
 		COMPLETE_WITH_LIST_CS(backslash_commands);
 
+#endif
+
+	}
+
 	/* If current word is a variable interpolation, handle that case */
 	else if (text[0] == ':' && text[1] != ':')
 	{
@@ -5757,7 +5820,7 @@ match_previous_words(int pattern_id,
 		else if (TailMatches("CREATE|ALTER|DROP", "USER", "MAPPING"))
 			COMPLETE_WITH("FOR");
 	}
-	else if (TailMatchesCS("\\l*") && !TailMatchesCS("\\lo*"))
+	else if (TailMatchesCS("\\list|\\l"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_databases);
 	else if (TailMatchesCS("\\password"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
@@ -5820,6 +5883,42 @@ match_previous_words(int pattern_id,
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
 	else if (TailMatchesCS("\\sv*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
+
+#ifdef LUA_SUPPORT
+
+	else if (TailMatchesCS("\\luafile"))
+		COMPLETE_WITH_FILES("\\", false);
+	else if (TailMatchesCS("\\lua|\\luastr"))
+	{
+		SimpleStringList globfunc = {NULL, NULL};
+		int			nglobfunc;
+
+		nglobfunc = lua_global_functions(lua, &globfunc);
+		if (nglobfunc > 0)
+		{
+			int		i ;
+			const char **globfunclist;
+			SimpleStringListCell *cell;
+
+			globfunclist = pg_malloc((nglobfunc + 1) * sizeof(char *));
+
+			for (i = 0, cell = globfunc.head; cell; cell = cell->next)
+			{
+				globfunclist[i] = cell->val;
+				i++;
+			}
+
+			globfunclist[i] = NULL;
+
+			COMPLETE_WITH_LIST_CS(globfunclist);
+
+			simple_string_list_destroy(&globfunc);
+			free(globfunclist);
+		}
+	}
+
+#endif
+
 	else if (TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|"
 						   "\\ir|\\include_relative|\\o|\\out|"
 						   "\\s|\\w|\\write|\\lo_import") ||
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 661c4a9b168..9ce5554426c 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -3,19 +3,19 @@
 /* Define if building universal (internal helper macro) */
 #undef AC_APPLE_UNIVERSAL_BUILD
 
-/* The normal alignment of `double', in bytes. */
+/* The normal alignment of 'double', in bytes. */
 #undef ALIGNOF_DOUBLE
 
-/* The normal alignment of `int', in bytes. */
+/* The normal alignment of 'int', in bytes. */
 #undef ALIGNOF_INT
 
-/* The normal alignment of `int64_t', in bytes. */
+/* The normal alignment of 'int64_t', in bytes. */
 #undef ALIGNOF_INT64_T
 
-/* The normal alignment of `PG_INT128_TYPE', in bytes. */
+/* The normal alignment of 'PG_INT128_TYPE', in bytes. */
 #undef ALIGNOF_PG_INT128_TYPE
 
-/* The normal alignment of `short', in bytes. */
+/* The normal alignment of 'short', in bytes. */
 #undef ALIGNOF_SHORT
 
 /* Size of a disk block --- this also limits the size of a tuple. You can set
@@ -48,22 +48,22 @@
 /* Define to 1 if you want National Language Support. (--enable-nls) */
 #undef ENABLE_NLS
 
-/* Define to 1 if you have the `append_history' function. */
+/* Define to 1 if you have the 'append_history' function. */
 #undef HAVE_APPEND_HISTORY
 
-/* Define to 1 if you have the `backtrace_symbols' function. */
+/* Define to 1 if you have the 'backtrace_symbols' function. */
 #undef HAVE_BACKTRACE_SYMBOLS
 
 /* Define to 1 if your compiler handles computed gotos. */
 #undef HAVE_COMPUTED_GOTO
 
-/* Define to 1 if you have the `copyfile' function. */
+/* Define to 1 if you have the 'copyfile' function. */
 #undef HAVE_COPYFILE
 
 /* Define to 1 if you have the <copyfile.h> header file. */
 #undef HAVE_COPYFILE_H
 
-/* Define to 1 if you have the `copy_file_range' function. */
+/* Define to 1 if you have the 'copy_file_range' function. */
 #undef HAVE_COPY_FILE_RANGE
 
 /* Define to 1 if you have the <crtdefs.h> header file. */
@@ -77,47 +77,47 @@
    similar. */
 #undef HAVE_CXX_TYPEOF_UNQUAL
 
-/* Define to 1 if you have the declaration of `fdatasync', and to 0 if you
+/* Define to 1 if you have the declaration of 'fdatasync', and to 0 if you
    don't. */
 #undef HAVE_DECL_FDATASYNC
 
-/* Define to 1 if you have the declaration of `F_FULLFSYNC', and to 0 if you
+/* Define to 1 if you have the declaration of 'F_FULLFSYNC', and to 0 if you
    don't. */
 #undef HAVE_DECL_F_FULLFSYNC
 
-/* Define to 1 if you have the declaration of `memset_s', and to 0 if you
+/* Define to 1 if you have the declaration of 'memset_s', and to 0 if you
    don't. */
 #undef HAVE_DECL_MEMSET_S
 
-/* Define to 1 if you have the declaration of `posix_fadvise', and to 0 if you
+/* Define to 1 if you have the declaration of 'posix_fadvise', and to 0 if you
    don't. */
 #undef HAVE_DECL_POSIX_FADVISE
 
-/* Define to 1 if you have the declaration of `preadv', and to 0 if you don't.
+/* Define to 1 if you have the declaration of 'preadv', and to 0 if you don't.
    */
 #undef HAVE_DECL_PREADV
 
-/* Define to 1 if you have the declaration of `pwritev', and to 0 if you
+/* Define to 1 if you have the declaration of 'pwritev', and to 0 if you
    don't. */
 #undef HAVE_DECL_PWRITEV
 
-/* Define to 1 if you have the declaration of `strchrnul', and to 0 if you
+/* Define to 1 if you have the declaration of 'strchrnul', and to 0 if you
    don't. */
 #undef HAVE_DECL_STRCHRNUL
 
-/* Define to 1 if you have the declaration of `strlcat', and to 0 if you
+/* Define to 1 if you have the declaration of 'strlcat', and to 0 if you
    don't. */
 #undef HAVE_DECL_STRLCAT
 
-/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you
+/* Define to 1 if you have the declaration of 'strlcpy', and to 0 if you
    don't. */
 #undef HAVE_DECL_STRLCPY
 
-/* Define to 1 if you have the declaration of `strsep', and to 0 if you don't.
+/* Define to 1 if you have the declaration of 'strsep', and to 0 if you don't.
    */
 #undef HAVE_DECL_STRSEP
 
-/* Define to 1 if you have the declaration of `timingsafe_bcmp', and to 0 if
+/* Define to 1 if you have the declaration of 'timingsafe_bcmp', and to 0 if
    you don't. */
 #undef HAVE_DECL_TIMINGSAFE_BCMP
 
@@ -127,19 +127,19 @@
 /* Define to 1 if you have the <editline/readline.h> header file. */
 #undef HAVE_EDITLINE_READLINE_H
 
-/* Define to 1 if you have the `elf_aux_info' function. */
+/* Define to 1 if you have the 'elf_aux_info' function. */
 #undef HAVE_ELF_AUX_INFO
 
 /* Define to 1 if you have the <execinfo.h> header file. */
 #undef HAVE_EXECINFO_H
 
-/* Define to 1 if you have the `explicit_bzero' function. */
+/* Define to 1 if you have the 'explicit_bzero' function. */
 #undef HAVE_EXPLICIT_BZERO
 
-/* Define to 1 if you have the `explicit_memset' function. */
+/* Define to 1 if you have the 'explicit_memset' function. */
 #undef HAVE_EXPLICIT_MEMSET
 
-/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+/* Define to 1 if fseeko (and ftello) are declared in stdio.h. */
 #undef HAVE_FSEEKO
 
 /* Define to 1 if you have __atomic_compare_exchange_n(int *, int *, int). */
@@ -162,25 +162,25 @@
    int64_t). */
 #undef HAVE_GCC__SYNC_INT64_CAS
 
-/* Define to 1 if you have the `getauxval' function. */
+/* Define to 1 if you have the 'getauxval' function. */
 #undef HAVE_GETAUXVAL
 
-/* Define to 1 if you have the `getifaddrs' function. */
+/* Define to 1 if you have the 'getifaddrs' function. */
 #undef HAVE_GETIFADDRS
 
-/* Define to 1 if you have the `getopt' function. */
+/* Define to 1 if you have the 'getopt' function. */
 #undef HAVE_GETOPT
 
 /* Define to 1 if you have the <getopt.h> header file. */
 #undef HAVE_GETOPT_H
 
-/* Define to 1 if you have the `getopt_long' function. */
+/* Define to 1 if you have the 'getopt_long' function. */
 #undef HAVE_GETOPT_LONG
 
-/* Define to 1 if you have the `getpeereid' function. */
+/* Define to 1 if you have the 'getpeereid' function. */
 #undef HAVE_GETPEEREID
 
-/* Define to 1 if you have the `getpeerucred' function. */
+/* Define to 1 if you have the 'getpeerucred' function. */
 #undef HAVE_GETPEERUCRED
 
 /* Define to 1 if you have the <gssapi_ext.h> header file. */
@@ -198,16 +198,16 @@
 /* Define to 1 if you have the <history.h> header file. */
 #undef HAVE_HISTORY_H
 
-/* Define to 1 if you have the `history_truncate_file' function. */
+/* Define to 1 if you have the 'history_truncate_file' function. */
 #undef HAVE_HISTORY_TRUNCATE_FILE
 
 /* Define to 1 if you have the <ifaddrs.h> header file. */
 #undef HAVE_IFADDRS_H
 
-/* Define to 1 if you have the `inet_aton' function. */
+/* Define to 1 if you have the 'inet_aton' function. */
 #undef HAVE_INET_ATON
 
-/* Define to 1 if you have the `inet_pton' function. */
+/* Define to 1 if you have the 'inet_pton' function. */
 #undef HAVE_INET_PTON
 
 /* Define to 1 if you have the <inttypes.h> header file. */
@@ -222,76 +222,88 @@
 /* Define to 1 if you have the global variable 'int timezone'. */
 #undef HAVE_INT_TIMEZONE
 
-/* Define to 1 if you have the `io_uring_queue_init_mem' function. */
+/* Define to 1 if you have the 'io_uring_queue_init_mem' function. */
 #undef HAVE_IO_URING_QUEUE_INIT_MEM
 
 /* Define to 1 if __builtin_constant_p(x) implies "i"(x) acceptance. */
 #undef HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P
 
-/* Define to 1 if you have the `kqueue' function. */
+/* Define to 1 if you have the 'kqueue' function. */
 #undef HAVE_KQUEUE
 
-/* Define to 1 if you have the `ldap_initialize' function. */
+/* Define to 1 if you have the <lauxlib.h> header file. */
+#undef HAVE_LAUXLIB_H
+
+/* Define to 1 if you have the 'ldap_initialize' function. */
 #undef HAVE_LDAP_INITIALIZE
 
-/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* Define to 1 if you have the 'crypto' library (-lcrypto). */
 #undef HAVE_LIBCRYPTO
 
 /* Define to 1 if you have the `curl' library (-lcurl). */
 #undef HAVE_LIBCURL
 
-/* Define to 1 if you have the `ldap' library (-lldap). */
+/* Define to 1 if you have the 'ldap' library (-lldap). */
 #undef HAVE_LIBLDAP
 
-/* Define to 1 if you have the `lz4' library (-llz4). */
+/* Define to 1 if you have the 'lz4' library (-llz4). */
 #undef HAVE_LIBLZ4
 
-/* Define to 1 if you have the `m' library (-lm). */
+/* Define to 1 if you have the 'm' library (-lm). */
 #undef HAVE_LIBM
 
-/* Define to 1 if you have the `numa' library (-lnuma). */
+/* Define to 1 if you have the 'numa' library (-lnuma). */
 #undef HAVE_LIBNUMA
 
-/* Define to 1 if you have the `pam' library (-lpam). */
+/* Define to 1 if you have the 'pam' library (-lpam). */
 #undef HAVE_LIBPAM
 
 /* Define if you have a function readline library */
 #undef HAVE_LIBREADLINE
 
-/* Define to 1 if you have the `selinux' library (-lselinux). */
+/* Define to 1 if you have the 'selinux' library (-lselinux). */
 #undef HAVE_LIBSELINUX
 
-/* Define to 1 if you have the `ssl' library (-lssl). */
+/* Define to 1 if you have the 'ssl' library (-lssl). */
 #undef HAVE_LIBSSL
 
-/* Define to 1 if you have the `wldap32' library (-lwldap32). */
+/* Define to 1 if you have the 'wldap32' library (-lwldap32). */
 #undef HAVE_LIBWLDAP32
 
-/* Define to 1 if you have the `xml2' library (-lxml2). */
+/* Define to 1 if you have the 'xml2' library (-lxml2). */
 #undef HAVE_LIBXML2
 
-/* Define to 1 if you have the `xslt' library (-lxslt). */
+/* Define to 1 if you have the 'xslt' library (-lxslt). */
 #undef HAVE_LIBXSLT
 
-/* Define to 1 if you have the `z' library (-lz). */
+/* Define to 1 if you have the 'z' library (-lz). */
 #undef HAVE_LIBZ
 
-/* Define to 1 if you have the `zstd' library (-lzstd). */
+/* Define to 1 if you have the 'zstd' library (-lzstd). */
 #undef HAVE_LIBZSTD
 
-/* Define to 1 if you have the `localeconv_l' function. */
+/* Define to 1 if you have the 'localeconv_l' function. */
 #undef HAVE_LOCALECONV_L
 
-/* Define to 1 if you have the `mbstowcs_l' function. */
-#undef HAVE_MBSTOWCS_L
+/* Define to use Lua */
+#undef HAVE_LUA
+
+/* Define to 1 if you have the <lualib.h> header file. */
+#undef HAVE_LUALIB_H
 
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_H
+/* Define to use Lua 5.1 */
+#undef HAVE_LUA_5_1
 
-/* Define to 1 if you have the `memset_explicit' function. */
+/* Define to 1 if you have the <lua.h> header file. */
+#undef HAVE_LUA_H
+
+/* Define to 1 if you have the 'mbstowcs_l' function. */
+#undef HAVE_MBSTOWCS_L
+
+/* Define to 1 if you have the 'memset_explicit' function. */
 #undef HAVE_MEMSET_EXPLICIT
 
-/* Define to 1 if you have the `mkdtemp' function. */
+/* Define to 1 if you have the 'mkdtemp' function. */
 #undef HAVE_MKDTEMP
 
 /* Define to 1 if you have the <ossp/uuid.h> header file. */
@@ -300,22 +312,22 @@
 /* Define to 1 if you have the <pam/pam_appl.h> header file. */
 #undef HAVE_PAM_PAM_APPL_H
 
-/* Define to 1 if you have the `posix_fadvise' function. */
+/* Define to 1 if you have the 'posix_fadvise' function. */
 #undef HAVE_POSIX_FADVISE
 
-/* Define to 1 if you have the `posix_fallocate' function. */
+/* Define to 1 if you have the 'posix_fallocate' function. */
 #undef HAVE_POSIX_FALLOCATE
 
-/* Define to 1 if you have the `ppoll' function. */
+/* Define to 1 if you have the 'ppoll' function. */
 #undef HAVE_PPOLL
 
 /* Define if you have POSIX threads libraries and header files. */
 #undef HAVE_PTHREAD
 
-/* Define to 1 if you have the `pthread_barrier_wait' function. */
+/* Define to 1 if you have the 'pthread_barrier_wait' function. */
 #undef HAVE_PTHREAD_BARRIER_WAIT
 
-/* Define to 1 if you have the `pthread_is_threaded_np' function. */
+/* Define to 1 if you have the 'pthread_is_threaded_np' function. */
 #undef HAVE_PTHREAD_IS_THREADED_NP
 
 /* Have PTHREAD_PRIO_INHERIT. */
@@ -330,14 +342,14 @@
 /* Define to 1 if you have the <readline/readline.h> header file. */
 #undef HAVE_READLINE_READLINE_H
 
-/* Define to 1 if you have the `rl_completion_matches' function. */
+/* Define to 1 if you have the 'rl_completion_matches' function. */
 #undef HAVE_RL_COMPLETION_MATCHES
 
 /* Define to 1 if you have the global variable 'rl_completion_suppress_quote'.
    */
 #undef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
 
-/* Define to 1 if you have the `rl_filename_completion_function' function. */
+/* Define to 1 if you have the 'rl_filename_completion_function' function. */
 #undef HAVE_RL_FILENAME_COMPLETION_FUNCTION
 
 /* Define to 1 if you have the global variable 'rl_filename_quote_characters'.
@@ -348,10 +360,10 @@
    */
 #undef HAVE_RL_FILENAME_QUOTING_FUNCTION
 
-/* Define to 1 if you have the `rl_reset_screen_size' function. */
+/* Define to 1 if you have the 'rl_reset_screen_size' function. */
 #undef HAVE_RL_RESET_SCREEN_SIZE
 
-/* Define to 1 if you have the `rl_variable_bind' function. */
+/* Define to 1 if you have the 'rl_variable_bind' function. */
 #undef HAVE_RL_VARIABLE_BIND
 
 /* Define to 1 if you have SA_SIGINFO available. */
@@ -360,37 +372,40 @@
 /* Define to 1 if you have the <security/pam_appl.h> header file. */
 #undef HAVE_SECURITY_PAM_APPL_H
 
-/* Define to 1 if you have the `setproctitle' function. */
+/* Define to 1 if you have the 'setproctitle' function. */
 #undef HAVE_SETPROCTITLE
 
-/* Define to 1 if you have the `setproctitle_fast' function. */
+/* Define to 1 if you have the 'setproctitle_fast' function. */
 #undef HAVE_SETPROCTITLE_FAST
 
-/* Define to 1 if the system has the type `socklen_t'. */
+/* Define to 1 if the system has the type 'socklen_t'. */
 #undef HAVE_SOCKLEN_T
 
-/* Define to 1 if you have the `SSL_CTX_set_cert_cb' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_cert_cb' function. */
 #undef HAVE_SSL_CTX_SET_CERT_CB
 
-/* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_ciphersuites' function. */
 #undef HAVE_SSL_CTX_SET_CIPHERSUITES
 
-/* Define to 1 if you have the `SSL_CTX_set_client_hello_cb' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_client_hello_cb' function. */
 #undef HAVE_SSL_CTX_SET_CLIENT_HELLO_CB
 
-/* Define to 1 if you have the `SSL_CTX_set_keylog_callback' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_keylog_callback' function. */
 #undef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
 
-/* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_num_tickets' function. */
 #undef HAVE_SSL_CTX_SET_NUM_TICKETS
 
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
 
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
 /* Define to 1 if you have the <stdlib.h> header file. */
 #undef HAVE_STDLIB_H
 
-/* Define to 1 if you have the `strerror_r' function. */
+/* Define to 1 if you have the 'strerror_r' function. */
 #undef HAVE_STRERROR_R
 
 /* Define to 1 if you have the <strings.h> header file. */
@@ -399,31 +414,31 @@
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
-/* Define to 1 if you have the `strlcat' function. */
+/* Define to 1 if you have the 'strlcat' function. */
 #undef HAVE_STRLCAT
 
-/* Define to 1 if you have the `strlcpy' function. */
+/* Define to 1 if you have the 'strlcpy' function. */
 #undef HAVE_STRLCPY
 
-/* Define to 1 if you have the `strsep' function. */
+/* Define to 1 if you have the 'strsep' function. */
 #undef HAVE_STRSEP
 
-/* Define to 1 if you have the `strsignal' function. */
+/* Define to 1 if you have the 'strsignal' function. */
 #undef HAVE_STRSIGNAL
 
-/* Define to 1 if the system has the type `struct option'. */
+/* Define to 1 if the system has the type 'struct option'. */
 #undef HAVE_STRUCT_OPTION
 
-/* Define to 1 if `sa_len' is a member of `struct sockaddr'. */
+/* Define to 1 if 'sa_len' is a member of 'struct sockaddr'. */
 #undef HAVE_STRUCT_SOCKADDR_SA_LEN
 
-/* Define to 1 if `tm_zone' is a member of `struct tm'. */
+/* Define to 1 if 'tm_zone' is a member of 'struct tm'. */
 #undef HAVE_STRUCT_TM_TM_ZONE
 
-/* Define to 1 if you have the `syncfs' function. */
+/* Define to 1 if you have the 'syncfs' function. */
 #undef HAVE_SYNCFS
 
-/* Define to 1 if you have the `sync_file_range' function. */
+/* Define to 1 if you have the 'sync_file_range' function. */
 #undef HAVE_SYNC_FILE_RANGE
 
 /* Define to 1 if you have the syslog interface. */
@@ -462,7 +477,7 @@
 /* Define to 1 if curl_global_init() is guaranteed to be thread-safe. */
 #undef HAVE_THREADSAFE_CURL_GLOBAL_INIT
 
-/* Define to 1 if you have the `timingsafe_bcmp' function. */
+/* Define to 1 if you have the 'timingsafe_bcmp' function. */
 #undef HAVE_TIMINGSAFE_BCMP
 
 /* Define to 1 if your compiler understands `typeof' or something similar. */
@@ -478,13 +493,13 @@
 /* Define to 1 if you have the <ucred.h> header file. */
 #undef HAVE_UCRED_H
 
-/* Define to 1 if the system has the type `union semun'. */
+/* Define to 1 if the system has the type 'union semun'. */
 #undef HAVE_UNION_SEMUN
 
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
-/* Define to 1 if you have the `uselocale' function. */
+/* Define to 1 if you have the 'uselocale' function. */
 #undef HAVE_USELOCALE
 
 /* Define to 1 if you have BSD UUID support. */
@@ -505,10 +520,10 @@
 /* Define to 1 if your compiler knows the visibility("hidden") attribute. */
 #undef HAVE_VISIBILITY_ATTRIBUTE
 
-/* Define to 1 if you have the `wcstombs_l' function. */
+/* Define to 1 if you have the 'wcstombs_l' function. */
 #undef HAVE_WCSTOMBS_L
 
-/* Define to 1 if you have the `X509_get_signature_info' function. */
+/* Define to 1 if you have the 'X509_get_signature_info' function. */
 #undef HAVE_X509_GET_SIGNATURE_INFO
 
 /* Define to 1 if the assembler supports X86_64's POPCNTQ instruction. */
@@ -641,25 +656,27 @@
    RELSEG_SIZE requires an initdb. */
 #undef RELSEG_SIZE
 
-/* The size of `intmax_t', as computed by sizeof. */
+/* The size of 'intmax_t', as computed by sizeof. */
 #undef SIZEOF_INTMAX_T
 
-/* The size of `long', as computed by sizeof. */
+/* The size of 'long', as computed by sizeof. */
 #undef SIZEOF_LONG
 
-/* The size of `long long', as computed by sizeof. */
+/* The size of 'long long', as computed by sizeof. */
 #undef SIZEOF_LONG_LONG
 
-/* The size of `off_t', as computed by sizeof. */
+/* The size of 'off_t', as computed by sizeof. */
 #undef SIZEOF_OFF_T
 
-/* The size of `size_t', as computed by sizeof. */
+/* The size of 'size_t', as computed by sizeof. */
 #undef SIZEOF_SIZE_T
 
-/* The size of `void *', as computed by sizeof. */
+/* The size of 'void *', as computed by sizeof. */
 #undef SIZEOF_VOID_P
 
-/* Define to 1 if you have the ANSI C header files. */
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
 #undef STDC_HEADERS
 
 /* Define to 1 if strerror_r() returns int. */
@@ -792,12 +809,18 @@
 /* Number of bits in a file offset, on hosts where this is settable. */
 #undef _FILE_OFFSET_BITS
 
-/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* Define to 1 if necessary to make fseeko visible. */
 #undef _LARGEFILE_SOURCE
 
-/* Define for large files, on AIX-style hosts. */
+/* Define to 1 on platforms where this makes off_t a 64-bit type. */
 #undef _LARGE_FILES
 
+/* Number of bits in time_t, on hosts where this is settable. */
+#undef _TIME_BITS
+
+/* Define to 1 on platforms where this makes time_t a 64-bit type. */
+#undef __MINGW_USE_VC2005_COMPAT
+
 /* Define to how the C++ compiler spells `typeof'. */
 #undef pg_cxx_typeof
 
@@ -810,14 +833,15 @@
 
 /* Define to the equivalent of the C99 'restrict' keyword, or to
    nothing if this is not supported.  Do not define if restrict is
-   supported directly.  */
+   supported only directly.  */
 #undef restrict
-/* Work around a bug in Sun C++: it does not support _Restrict or
-   __restrict__, even though the corresponding Sun C compiler ends up with
-   "#define restrict _Restrict" or "#define restrict __restrict__" in the
-   previous line.  Perhaps some future version of Sun C++ will work with
-   restrict; if so, hopefully it defines __RESTRICT like Sun C does.  */
-#if defined __SUNPRO_CC && !defined __RESTRICT
+/* Work around a bug in older versions of Sun C++, which did not
+   #define __restrict__ or support _Restrict or __restrict__
+   even though the corresponding Sun C compiler ended up with
+   "#define restrict _Restrict" or "#define restrict __restrict__"
+   in the previous line.  This workaround can be removed once
+   we assume Oracle Developer Studio 12.5 (2016) or later.  */
+#if defined __SUNPRO_CC && !defined __RESTRICT && !defined __restrict__
 # define _Restrict
 # define __restrict__
 #endif
diff --git a/src/test/regress/expected/lua.out b/src/test/regress/expected/lua.out
new file mode 100644
index 00000000000..b85701e8069
--- /dev/null
+++ b/src/test/regress/expected/lua.out
@@ -0,0 +1,58 @@
+\if :{?LUA_RELEASE}
+  \echo "lua is supported"
+"lua is supported"
+\else
+  \echo "lua is not supported"
+  \quit
+\endif
+-- lua commands
+\luacode
+\luacode
+11	ahoj
+\lua foo 10 ahoj
+11	ahoj
+\luaset myvar foo 10 ahoj
+\echo :myvar
+11
+\unset myvar
+create table footable(a int, b int);
+insert into footable values(10, 20);
+insert into footable values(30, 40);
+\luacode
+ a  | b  
+----+----
+ 10 | 20
+ 30 | 40
+(2 rows)
+
+ a  | b  
+----+----
+ 10 | 20
+ 30 | 40
+(2 rows)
+
+\set tablename footable
+\luastr psql.printQuery(psql.exec("select * from " .. :"tablename"))
+ a  | b  
+----+----
+ 10 | 20
+ 30 | 40
+(2 rows)
+
+drop table footable;
+\luacode
+\luaset a,b,c foo 10 20 30
+\echo :a :b :c
+10 20 30
+\luaset a , b , c foo 10 20 30
+\echo :a :b :c
+10 20 30
+\luaset a ,b , c foo 10 20 30
+\echo :a :b :c
+10 20 30
+\luaset a,b , c foo 10 20 30
+\echo :a :b :c
+10 20 30
+\luaset a ,b, c foo 10 20 30
+\echo :a :b :c
+10 20 30
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 8356ca98ef2..c01bafd91a7 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -81,7 +81,7 @@ test: create_table_like alter_generic alter_operator misc async dbsize merge mis
 # collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
-test: rules psql psql_crosstab psql_pipeline amutils stats_ext collate.linux.utf8 collate.windows.win1252
+test: rules psql lua psql_crosstab psql_pipeline amutils stats_ext collate.linux.utf8 collate.windows.win1252
 
 # ----------
 # Run these alone so they don't run out of parallel workers
diff --git a/src/test/regress/sql/lua.sql b/src/test/regress/sql/lua.sql
new file mode 100644
index 00000000000..5aad48f39e1
--- /dev/null
+++ b/src/test/regress/sql/lua.sql
@@ -0,0 +1,58 @@
+\if :{?LUA_RELEASE}
+  \echo "lua is supported"
+\else
+  \echo "lua is not supported"
+  \quit
+\endif
+
+-- lua commands
+\luacode
+function foo(n, m)
+  return n + 1, m
+end;
+\.
+
+\luacode
+print(foo(10, "ahoj"))
+\.
+
+\lua foo 10 ahoj
+\luaset myvar foo 10 ahoj
+\echo :myvar
+\unset myvar
+
+create table footable(a int, b int);
+insert into footable values(10, 20);
+insert into footable values(30, 40);
+
+\luacode
+  local con = psql.connect();
+  psql.printQuery(con:exec("select * from footable"));
+  psql.printQuery(psql.exec("select * from footable"));
+\.
+
+\set tablename footable
+\luastr psql.printQuery(psql.exec("select * from " .. :"tablename"))
+
+drop table footable;
+
+\luacode
+function foo(a, b, c)
+  return a, b, c;
+end
+\.
+
+\luaset a,b,c foo 10 20 30
+\echo :a :b :c
+\luaset a , b , c foo 10 20 30
+\echo :a :b :c
+
+\luaset a ,b , c foo 10 20 30
+\echo :a :b :c
+
+\luaset a,b , c foo 10 20 30
+\echo :a :b :c
+
+\luaset a ,b, c foo 10 20 30
+\echo :a :b :c
+
-- 
2.55.0

