--- a/src/spi.c
+++ b/src/spi.c
@@ -272,7 +272,11 @@ static void pllua_spi_prepare_checkparam
 static void pllua_spi_prepare_parser_setup_hook(ParseState *pstate, void *arg)
 {
 	pllua_spi_statement *stmt = arg;
+#if PG_VERSION_NUM >= 150000
+	setup_parse_variable_parameters(pstate, &stmt->param_types, &stmt->param_types_len);
+#else
 	parse_variable_parameters(pstate, &stmt->param_types, &stmt->param_types_len);
+#endif
 }
 
 static pllua_spi_statement *pllua_spi_make_statement(lua_State *L,
--- a/src/time.c
+++ b/src/time.c
@@ -920,15 +920,33 @@ pllua_time_as_table(lua_State *L)
 
 		case INTERVALOID:
 			{
+#if PG_VERSION_NUM >= 150000
+				struct pg_itm	itm = { 0 };
+#endif
 				Interval *itmp = DatumGetIntervalP(val);
 				PLLUA_TRY();
 				{
+#if PG_VERSION_NUM >= 150000
+					/* PG15 removes interval2tm, use interval2itm and copy the result to tm */
+					interval2itm(*itmp, &itm);
+					tm.tm_sec  = itm.tm_sec;
+					tm.tm_min  = itm.tm_min;
+					tm.tm_hour = itm.tm_hour;
+					tm.tm_mday = itm.tm_mday;
+					tm.tm_mon  = itm.tm_mon;
+					tm.tm_year = itm.tm_year;
+#else
 					if (interval2tm(*itmp, &tm, &fsec) != 0)
 						elog(ERROR, "interval output failed");
+#endif
 				}
 				PLLUA_CATCH_RETHROW();
 
+#if PG_VERSION_NUM >= 150000
+				microsecs = FSEC_T_SCALE(itm.tm_usec);
+#else
 				microsecs = FSEC_T_SCALE(fsec);
+#endif
 			}
 			break;
 	}
--- /dev/null
+++ b/expected/horology_1.out
@@ -0,0 +1,606 @@
+--
+\set VERBOSITY terse
+--
+-- 1. Input of datetime values from table form.
+set timezone = 'GMT';
+set datestyle = 'ISO,YMD';
+do language pllua $$
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamptz{ year=2419, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamptz{ year=1919, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamptz{ year=1819, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamptz{ year=1019, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamptz{ year=-2019, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamptz{ year=-4713, month=12, day=1, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34, usec=123456 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, msec=1, usec=1 });
+$$;
+INFO:  2019-04-22 12:23:34.1+00
+INFO:  2419-04-22 12:23:34.1+00
+INFO:  1919-04-22 12:23:34.1+00
+INFO:  1819-04-22 12:23:34.1+00
+INFO:  1019-04-22 12:23:34.1+00
+INFO:  2020-04-22 12:23:34.1+00 BC
+INFO:  4714-12-01 12:23:34.1+00 BC
+INFO:  2019-04-22 12:23:34.123456+00
+INFO:  2019-04-22 12:23:34.101001+00
+do language pllua $$
+  print(pgtype.timestamptz{ epoch=0 });
+  print(pgtype.timestamptz{ epoch=1555891200 });
+  print(pgtype.timestamptz{ epoch=1555891200.000001 });
+  print(pgtype.timestamptz{ epoch_msec=1555891200001 });
+  print(pgtype.timestamptz{ epoch_usec=1555891200000001 });
+  print(pgtype.timestamptz{ epoch=1555891200, msec=1, usec=1 });
+  print(pgtype.timestamptz{ epoch=1555891200, msec=-1, usec=1 });
+  print(pgtype.timestamptz{ epoch=-1555891200 });
+  print(pgtype.timestamptz{ epoch=-1555891200, msec=1, usec=-1 });
+  print(pgtype.timestamptz{ epoch=-1555891200, msec=-1, usec=1 });
+  print(pgtype.timestamptz{ epoch_msec=-1555891200001 });
+  print(pgtype.timestamptz{ epoch_usec=-1555891200000001 });
+$$;
+INFO:  1970-01-01 00:00:00+00
+INFO:  2019-04-22 00:00:00+00
+INFO:  2019-04-22 00:00:00.000001+00
+INFO:  2019-04-22 00:00:00.001+00
+INFO:  2019-04-22 00:00:00.000001+00
+INFO:  2019-04-22 00:00:00.001001+00
+INFO:  2019-04-21 23:59:59.999001+00
+INFO:  1920-09-12 00:00:00+00
+INFO:  1920-09-12 00:00:00.000999+00
+INFO:  1920-09-11 23:59:59.999001+00
+INFO:  1920-09-11 23:59:59.999+00
+INFO:  1920-09-11 23:59:59.999999+00
+do language pllua $$
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone=10800 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone=-10800 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone="+0300" });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone="-0300" });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone="+1400" });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone="-1400" });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone="America/Los_Angeles" });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone="Pacific/Auckland" });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, timezone="Asia/Kathmandu" });
+  print(pgtype.timestamptz{ year=2018, month=10, day=28, hour=1, min=30, sec=0, timezone="Europe/London" });
+  print(pgtype.timestamptz{ year=2018, month=10, day=28, hour=1, min=30, sec=0, isdst=true, timezone="Europe/London" });
+  print(pgtype.timestamptz{ year=2018, month=10, day=28, hour=1, min=30, sec=0, isdst=false, timezone="Europe/London" });
+$$;
+INFO:  2019-04-22 09:23:34.1+00
+INFO:  2019-04-22 15:23:34.1+00
+INFO:  2019-04-22 09:23:34.1+00
+INFO:  2019-04-22 15:23:34.1+00
+INFO:  2019-04-21 22:23:34.1+00
+INFO:  2019-04-23 02:23:34.1+00
+INFO:  2019-04-22 19:23:34.1+00
+INFO:  2019-04-22 00:23:34.1+00
+INFO:  2019-04-22 06:38:34.1+00
+INFO:  2018-10-28 01:30:00+00
+INFO:  2018-10-28 00:30:00+00
+INFO:  2018-10-28 01:30:00+00
+do language pllua $$
+  print(pgtype.timestamptz{ epoch=1/0 });
+  print(pgtype.timestamptz{ epoch=-1/0 });
+$$;
+INFO:  infinity
+INFO:  -infinity
+do language pllua $$
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=1, usec=59000000 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=-1 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=-60 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=-61 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=60 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=61 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=23, sec=120 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=0, sec=3661 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=0, sec=43201 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=0, sec=-43201 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=0, sec=864000 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=12, min=720, sec=3661 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=24, min=0, sec=0 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=24, min=0, sec=1 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=240, min=0, sec=1 });
+  print(pgtype.timestamptz{ year=2019, month=4, day=22, hour=-1, min=0, sec=1 });
+  print(pgtype.timestamptz{ year=2019, month=-4, day=22, hour=12, min=23, sec=34 });
+  print(pgtype.timestamptz{ year=2019, month=16, day=22, hour=12, min=23, sec=34 });
+$$;
+INFO:  2019-04-22 12:24:00+00
+INFO:  2019-04-22 12:22:59+00
+INFO:  2019-04-22 12:22:00+00
+INFO:  2019-04-22 12:21:59+00
+INFO:  2019-04-22 12:24:00+00
+INFO:  2019-04-22 12:24:01+00
+INFO:  2019-04-22 12:25:00+00
+INFO:  2019-04-22 13:01:01+00
+INFO:  2019-04-23 00:00:01+00
+INFO:  2019-04-21 23:59:59+00
+INFO:  2019-05-02 12:00:00+00
+INFO:  2019-04-23 01:01:01+00
+INFO:  2019-04-23 00:00:00+00
+INFO:  2019-04-23 00:00:01+00
+INFO:  2019-05-02 00:00:01+00
+INFO:  2019-04-21 23:00:01+00
+INFO:  2018-08-22 12:23:34+00
+INFO:  2020-04-22 12:23:34+00
+do language pllua $$
+  print(pgtype.timestamp{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamp{ year=2419, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamp{ year=1919, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamp{ year=1819, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamp{ year=1019, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamp{ year=-2019, month=4, day=22, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamp{ year=-4713, month=12, day=1, hour=12, min=23, sec=34.1 });
+  print(pgtype.timestamp{ year=2019, month=4, day=22, hour=12, min=23, sec=34, usec=123456 });
+  print(pgtype.timestamp{ year=2019, month=4, day=22, hour=12, min=23, sec=34.1, msec=1, usec=1 });
+$$;
+INFO:  2019-04-22 12:23:34.1
+INFO:  2419-04-22 12:23:34.1
+INFO:  1919-04-22 12:23:34.1
+INFO:  1819-04-22 12:23:34.1
+INFO:  1019-04-22 12:23:34.1
+INFO:  2020-04-22 12:23:34.1 BC
+INFO:  4714-12-01 12:23:34.1 BC
+INFO:  2019-04-22 12:23:34.123456
+INFO:  2019-04-22 12:23:34.101001
+do language pllua $$
+  print(pgtype.timestamp{ epoch=0 });
+  print(pgtype.timestamp{ epoch=1555891200 });
+  print(pgtype.timestamp{ epoch=1555891200.000001 });
+  print(pgtype.timestamp{ epoch_msec=1555891200001 });
+  print(pgtype.timestamp{ epoch_usec=1555891200000001 });
+  print(pgtype.timestamp{ epoch=1555891200, msec=1, usec=1 });
+  print(pgtype.timestamp{ epoch=1555891200, msec=-1, usec=1 });
+  print(pgtype.timestamp{ epoch=-1555891200 });
+  print(pgtype.timestamp{ epoch=-1555891200, msec=1, usec=-1 });
+  print(pgtype.timestamp{ epoch=-1555891200, msec=-1, usec=1 });
+  print(pgtype.timestamp{ epoch_msec=-1555891200001 });
+  print(pgtype.timestamp{ epoch_usec=-1555891200000001 });
+  print(pgtype.timestamp{ epoch=1555891200, timezone="America/Los_Angeles" });
+  print(pgtype.timestamp{ epoch=1555891200, timezone="Pacific/Auckland" });
+  print(pgtype.timestamp{ epoch=1555891200, timezone="Asia/Kathmandu" });
+$$;
+INFO:  1970-01-01 00:00:00
+INFO:  2019-04-22 00:00:00
+INFO:  2019-04-22 00:00:00.000001
+INFO:  2019-04-22 00:00:00.001
+INFO:  2019-04-22 00:00:00.000001
+INFO:  2019-04-22 00:00:00.001001
+INFO:  2019-04-21 23:59:59.999001
+INFO:  1920-09-12 00:00:00
+INFO:  1920-09-12 00:00:00.000999
+INFO:  1920-09-11 23:59:59.999001
+INFO:  1920-09-11 23:59:59.999
+INFO:  1920-09-11 23:59:59.999999
+INFO:  2019-04-21 17:00:00
+INFO:  2019-04-22 12:00:00
+INFO:  2019-04-22 05:45:00
+do language pllua $$
+  print(pgtype.date{ year=2019, month=4, day=22 });
+  print(pgtype.date{ year=2419, month=2, day=22 });
+  print(pgtype.date{ year=1919, month=1, day=22 });
+  print(pgtype.date{ year=1819, month=3, day=22 });
+  print(pgtype.date{ year=1019, month=5, day=22 });
+  print(pgtype.date{ year=-2019, month=4, day=22 });
+  print(pgtype.date{ year=-4713, month=12, day=1 });
+  print(pgtype.date{ year=2019, month=4, day=22, hour=24 });
+  print(pgtype.date{ year=2019, month=4, day=22, hour=24, min=1 });
+$$;
+INFO:  2019-04-22
+INFO:  2419-02-22
+INFO:  1919-01-22
+INFO:  1819-03-22
+INFO:  1019-05-22
+INFO:  2020-04-22 BC
+INFO:  4714-12-01 BC
+INFO:  2019-04-22
+INFO:  2019-04-23
+do language pllua $$
+  print(pgtype.date{ epoch=0 });
+  print(pgtype.date{ epoch=1555891200 });
+  print(pgtype.date{ epoch=1555891200.000001 });
+  print(pgtype.date{ epoch_msec=1555891200001 });
+  print(pgtype.date{ epoch_usec=1555891200000001 });
+  print(pgtype.date{ epoch=1555891200 });
+  print(pgtype.date{ epoch=-1555891200 });
+  print(pgtype.date{ epoch_msec=-1555891200001 });
+  print(pgtype.date{ epoch_usec=-1555891200000001 });
+  print(pgtype.date{ epoch=1555891200, timezone="America/Los_Angeles" });
+  print(pgtype.date{ epoch=1555891200, timezone="Pacific/Auckland" });
+  print(pgtype.date{ epoch=1555891200, timezone="Asia/Kathmandu" });
+$$;
+INFO:  1970-01-01
+INFO:  2019-04-22
+INFO:  2019-04-22
+INFO:  2019-04-22
+INFO:  2019-04-22
+INFO:  2019-04-22
+INFO:  1920-09-12
+INFO:  1920-09-11
+INFO:  1920-09-11
+INFO:  2019-04-21
+INFO:  2019-04-22
+INFO:  2019-04-22
+do language pllua $$
+  print(pgtype.time{ hour=12, min=23, sec=34.1 });
+  print(pgtype.time{ hour=12, min=120, sec=1 });
+  print(pgtype.time{ hour=24, min=23, sec=34.1 });
+  print(pgtype.time{ hour=25, min=23, sec=34.1 });
+  print(pgtype.time{ hour=12, min=23, sec=34, usec=123456 });
+  print(pgtype.time{ hour=12, min=23, sec=34.1, msec=1, usec=1 });
+$$;
+INFO:  12:23:34.1
+INFO:  14:00:01
+INFO:  00:23:34.1
+INFO:  01:23:34.1
+INFO:  12:23:34.123456
+INFO:  12:23:34.101001
+do language pllua $$
+  print(pgtype.time{ epoch=0 });
+  print(pgtype.time{ epoch=3601 });
+  print(pgtype.time{ epoch=86400 });
+  print(pgtype.time{ epoch=1555891200 });
+$$;
+INFO:  00:00:00
+INFO:  01:00:01
+INFO:  00:00:00
+INFO:  00:00:00
+do language pllua $$
+  print(pgtype.timetz{ hour=12, min=23, sec=34.1, timezone=7200 });
+$$;
+INFO:  12:23:34.1+02
+do language pllua $$
+  print(pgtype.timetz{ epoch=0, timezone=3600 });
+  print(pgtype.timetz{ epoch=3601, timezone=-3600 });
+  print(pgtype.timetz{ epoch=86400, timezone=-43200 });
+  print(pgtype.timetz{ epoch=1555891200, timezone=0 });
+$$;
+INFO:  00:00:00+01
+INFO:  01:00:01-01
+INFO:  00:00:00-12
+INFO:  00:00:00+00
+do language pllua $$
+  print(pgtype.interval{ year=0, month=0, day=0, hour=0, min=0, sec=0, usec=0 });
+  print(pgtype.interval{ year=100 });
+  print(pgtype.interval{ month=100 });
+  print(pgtype.interval{ day=100 });
+  print(pgtype.interval{ hour=100 });
+  print(pgtype.interval{ min=100 });
+  print(pgtype.interval{ sec=100 });
+  print(pgtype.interval{ usec=1 });
+  print(pgtype.interval{ year=1, month=2, day=3, hour=4, min=5, sec=6, usec=7 });
+$$;
+INFO:  @ 0
+INFO:  @ 100 years
+INFO:  @ 8 years 4 mons
+INFO:  @ 100 days
+INFO:  @ 100 hours
+INFO:  @ 1 hour 40 mins
+INFO:  @ 1 min 40 secs
+INFO:  @ 0.000001 secs
+INFO:  @ 1 year 2 mons 3 days 4 hours 5 mins 6.000007 secs
+do language pllua $$
+  print(pgtype.interval{ epoch=0 });
+  print(pgtype.interval{ epoch=120 });
+  print(pgtype.interval{ epoch=43200 });
+  print(pgtype.interval{ epoch=86400 });
+$$;
+INFO:  @ 0
+INFO:  @ 2 mins
+INFO:  @ 12 hours
+INFO:  @ 24 hours
+-- input error cases.
+do language pllua $$ print(pgtype.interval{ hour="foo" }); $$;
+ERROR:  pllua: invalid value in field 'hour'
+do language pllua $$ print(pgtype.interval{ hour=1.2 }); $$;
+ERROR:  pllua: invalid value in field 'hour'
+do language pllua $$ print(pgtype.interval{ hour=0/0 }); $$;
+ERROR:  pllua: invalid value in field 'hour'
+do language pllua $$ print(pgtype.interval{ hour=1/0 }); $$;
+ERROR:  pllua: infinite values not permitted for this type
+do language pllua $$ print(pgtype.interval{ sec=1/0 }); $$;
+ERROR:  pllua: infinite values not permitted for this type
+do language pllua $$ print(pgtype.interval{ usec=1/0 }); $$;
+ERROR:  pllua: infinite values not permitted for this type
+do language pllua $$ print(pgtype.timestamptz{ year=1/0, month=1, day=-1/0 }); $$;
+ERROR:  pllua: invalid value in field 'day'
+do language pllua $$ print(pgtype.timestamp{ epoch=0, timezone="1234" }); $$;
+ERROR:  invalid timezone specified
+do language pllua $$ print(pgtype.timestamp{ epoch=0, timezone=1234.5 }); $$;
+ERROR:  pllua: invalid value in field 'timezone'
+do language pllua $$ print(pgtype.timestamp{ epoch=0, timezone=function() end }); $$;
+ERROR:  pllua: invalid value in field 'timezone'
+do language pllua $$ print(pgtype.timestamptz{ epoch=0, epoch_msec=0 }); $$;
+ERROR:  pllua: cannot specify multiple epoch fields
+do language pllua $$ print(pgtype.timestamptz{ epoch=0, year=2019 }); $$;
+ERROR:  pllua: cannot specify both epoch and date fields
+do language pllua $$ print(pgtype.timestamptz{ epoch=0, hour=20 }); $$;
+ERROR:  pllua: cannot specify both epoch and time fields
+do language pllua $$ print(pgtype.timestamptz{ year=2019 }); $$;
+ERROR:  pllua: missing datetime field 'mon'
+do language pllua $$ print(pgtype.timestamptz{ year=2019, month=4 }); $$;
+ERROR:  pllua: missing datetime field 'day'
+do language pllua $$ print(pgtype.time{ min=10 }); $$;
+ERROR:  pllua: missing datetime field 'hour'
+do language pllua $$ print(pgtype.time{ hour=20, sec=10 }); $$;
+ERROR:  pllua: missing datetime field 'min'
+do language pllua $$ print(pgtype.timetz{ hour=20, timezone="America/Los_Angeles" }); $$;
+ERROR:  pllua: non-numeric timezones not supported for 'timetz'
+do language pllua $$ print(pgtype.time{ hour=20, timezone="America/Los_Angeles" }); $$;
+ERROR:  pllua: cannot specify timezone for this type
+do language pllua $$ print(pgtype.date{ year=2019, month=4, day=22, timezone="America/Los_Angeles" }); $$;
+ERROR:  pllua: cannot specify timezone for this type
+do language pllua $$ print(pgtype.timestamp{ year=2019, month=4, day=22, hour=20, timezone="America/Los_Angeles" }); $$;
+ERROR:  pllua: cannot specify timezone for this type
+do language pllua $$ print(pgtype.timestamptz{ epoch=-300000000000 }); $$;
+ERROR:  timestamp out of range
+do language pllua $$ print(pgtype.timestamptz{ year=-5000, month=1, day=1 }); $$;
+ERROR:  could not convert to timestamp
+do language pllua $$ print(pgtype.date{ year=-5000, month=1, day=1 }); $$;
+INFO:  4760-06-20 BC
+-- 2. output of values in table form.
+do language pllua $$
+  local function prt(t,z)
+    print(t)
+    local rt = t:as_table(z)
+    local o = {}
+    for k,_ in pairs(rt) do o[1+#o] = k end
+    table.sort(o)
+    for _,k in ipairs(o) do print(k,rt[k]) end
+  end
+  prt(pgtype.timestamptz('2019-04-22 10:20:30+00'))
+  prt(pgtype.timestamptz('2019-04-22 10:20:30+00'),'America/Los_Angeles')
+  prt(pgtype.timestamptz('2019-04-22 10:20:30+00'),'Asia/Kathmandu')
+  prt(pgtype.timestamp('2019-04-22 10:20:30+00'))
+  prt(pgtype.date('2019-04-22'))
+  prt(pgtype.time('10:20:30'))
+  prt(pgtype.timetz('10:20:30+04'))
+  prt(pgtype.interval('P1Y2M3DT4H5M6S'))
+$$;
+INFO:  2019-04-22 10:20:30+00
+INFO:  day	22
+INFO:  hour	10
+INFO:  isdst	false
+INFO:  min	20
+INFO:  month	4
+INFO:  sec	30
+INFO:  timezone	0
+INFO:  timezone_abbrev	GMT
+INFO:  usec	0
+INFO:  year	2019
+INFO:  2019-04-22 10:20:30+00
+INFO:  day	22
+INFO:  hour	3
+INFO:  isdst	true
+INFO:  min	20
+INFO:  month	4
+INFO:  sec	30
+INFO:  timezone	-25200
+INFO:  timezone_abbrev	PDT
+INFO:  usec	0
+INFO:  year	2019
+INFO:  2019-04-22 10:20:30+00
+INFO:  day	22
+INFO:  hour	16
+INFO:  isdst	false
+INFO:  min	5
+INFO:  month	4
+INFO:  sec	30
+INFO:  timezone	20700
+INFO:  timezone_abbrev	+0545
+INFO:  usec	0
+INFO:  year	2019
+INFO:  2019-04-22 10:20:30
+INFO:  day	22
+INFO:  hour	10
+INFO:  min	20
+INFO:  month	4
+INFO:  sec	30
+INFO:  usec	0
+INFO:  year	2019
+INFO:  2019-04-22
+INFO:  day	22
+INFO:  month	4
+INFO:  year	2019
+INFO:  10:20:30
+INFO:  hour	10
+INFO:  min	20
+INFO:  sec	30
+INFO:  usec	0
+INFO:  10:20:30+04
+INFO:  hour	10
+INFO:  min	20
+INFO:  sec	30
+INFO:  timezone	14400
+INFO:  usec	0
+INFO:  @ 1 year 2 mons 3 days 4 hours 5 mins 6 secs
+INFO:  day	3
+INFO:  hour	4
+INFO:  min	5
+INFO:  month	2
+INFO:  sec	6
+INFO:  usec	0
+INFO:  year	1
+-- error
+do language pllua $$ print(pgtype.timestamp('2019-04-22 10:20:30+00'):as_table('Europe/London')) $$;
+ERROR:  pllua: [string "DO-block"]:1: cannot specify timezone parameter for this type
+-- 3. Field access
+-- note, fields come out in session timezone, so set that:
+set timezone = 'Europe/London';
+do language pllua $$
+  local t = pgtype.timestamptz('1968-05-10 03:45:01.234567+01')
+
+  for _,k in ipairs{ 'century', 'day', 'decade', 'dow', 'doy',
+                     'epoch', 'epoch_msec', 'epoch_usec',
+		     'hour', 'isodow', 'isoweek', 'isoyear',
+		     'julian', 'microseconds', 'millennium',
+		     'milliseconds', 'minute', 'month',
+		     'quarter', 'second', 'timezone',
+		     'timezone_hour', 'timezone_minute',
+		     'week', 'year' } do
+    print(k, t[k])
+  end
+$$;
+INFO:  century	20
+INFO:  day	10
+INFO:  decade	196
+INFO:  dow	5
+INFO:  doy	131
+INFO:  epoch	-51916498.765433
+INFO:  epoch_msec	-51916498765.433
+INFO:  epoch_usec	-51916498765433
+INFO:  hour	3
+INFO:  isodow	5
+INFO:  isoweek	19
+INFO:  isoyear	1968
+INFO:  julian	2439987
+INFO:  microseconds	1234567
+INFO:  millennium	2
+INFO:  milliseconds	1235
+INFO:  minute	45
+INFO:  month	5
+INFO:  quarter	2
+INFO:  second	1.234567
+INFO:  timezone	3600
+INFO:  timezone_hour	1
+INFO:  timezone_minute	0
+INFO:  week	19
+INFO:  year	1968
+set timezone = 'UTC';
+do language pllua $$
+  local t = pgtype.timestamp('1968-05-10 03:45:01.234567')
+
+  for _,k in ipairs{ 'century', 'day', 'decade', 'dow', 'doy',
+                     'epoch', 'epoch_msec', 'epoch_usec',
+		     'hour', 'isodow', 'isoweek', 'isoyear',
+		     'julian', 'microseconds', 'millennium',
+		     'milliseconds', 'minute', 'month',
+		     'quarter', 'second',
+		     'week', 'year' } do
+    print(k, t[k])
+  end
+$$;
+INFO:  century	20
+INFO:  day	10
+INFO:  decade	196
+INFO:  dow	5
+INFO:  doy	131
+INFO:  epoch	-51912898.765433
+INFO:  epoch_msec	-51912898765.433
+INFO:  epoch_usec	-51912898765433
+INFO:  hour	3
+INFO:  isodow	5
+INFO:  isoweek	19
+INFO:  isoyear	1968
+INFO:  julian	2439987
+INFO:  microseconds	1234567
+INFO:  millennium	2
+INFO:  milliseconds	1235
+INFO:  minute	45
+INFO:  month	5
+INFO:  quarter	2
+INFO:  second	1.234567
+INFO:  week	19
+INFO:  year	1968
+do language pllua $$
+  local t = pgtype.date('1968-05-10')
+
+  for _,k in ipairs{ 'century', 'day', 'decade', 'dow', 'doy',
+                     'epoch', 'epoch_msec', 'epoch_usec',
+		     'hour', 'isodow', 'isoweek', 'isoyear',
+		     'julian', 'microseconds', 'millennium',
+		     'milliseconds', 'minute', 'month',
+		     'quarter', 'second',
+		     'week', 'year' } do
+    print(k, string.format("%.18g",t[k]))
+  end
+$$;
+INFO:  century	20
+INFO:  day	10
+INFO:  decade	196
+INFO:  dow	5
+INFO:  doy	131
+INFO:  epoch	-51926400
+INFO:  epoch_msec	-51926400000
+INFO:  epoch_usec	-51926400000000
+INFO:  hour	0
+INFO:  isodow	5
+INFO:  isoweek	19
+INFO:  isoyear	1968
+INFO:  julian	2439987
+INFO:  microseconds	0
+INFO:  millennium	2
+INFO:  milliseconds	0
+INFO:  minute	0
+INFO:  month	5
+INFO:  quarter	2
+INFO:  second	0
+INFO:  week	19
+INFO:  year	1968
+do language pllua $$
+  local t = pgtype.time('03:45:01.234567')
+
+  for _,k in ipairs{ 'epoch', 'epoch_msec', 'epoch_usec',
+		     'hour', 'microseconds',
+		     'milliseconds', 'minute', 'second' } do
+    print(k, t[k])
+  end
+$$;
+INFO:  epoch	13501.234567
+INFO:  epoch_msec	13501234.567
+INFO:  epoch_usec	13501234567
+INFO:  hour	3
+INFO:  microseconds	1234567
+INFO:  milliseconds	1235
+INFO:  minute	45
+INFO:  second	1.234567
+do language pllua $$
+  local t = pgtype.timetz('03:45:01.234567+01')
+
+  for _,k in ipairs{ 'epoch', 'epoch_msec', 'epoch_usec',
+		     'hour', 'microseconds',
+		     'milliseconds', 'minute', 'second',
+		     'timezone', 'timezone_hour',
+		     'timezone_minute', } do
+    print(k, t[k])
+  end
+$$;
+INFO:  epoch	9901.234567
+INFO:  epoch_msec	9901234.567
+INFO:  epoch_usec	9901234567
+INFO:  hour	3
+INFO:  microseconds	1234567
+INFO:  milliseconds	1235
+INFO:  minute	45
+INFO:  second	1.234567
+INFO:  timezone	3600
+INFO:  timezone_hour	1
+INFO:  timezone_minute	0
+do language pllua $$
+  local t = pgtype.interval('P1Y2M3DT4H5M6.789001S')
+
+  for _,k in ipairs{ 'century', 'day', 'decade',
+                     'epoch', 'epoch_msec', 'epoch_usec',
+		     'hour', 'microseconds', 'millennium',
+		     'milliseconds', 'minute', 'month',
+		     'quarter', 'second',
+		     'year' } do
+    print(k, t[k])
+  end
+$$;
+INFO:  century	0
+INFO:  day	3
+INFO:  decade	0
+INFO:  epoch	37015506.789001
+INFO:  epoch_msec	37015506789.001
+INFO:  epoch_usec	37015506789001
+INFO:  hour	4
+INFO:  microseconds	6789001
+INFO:  millennium	0
+INFO:  milliseconds	6789
+INFO:  minute	5
+INFO:  month	2
+INFO:  quarter	1
+INFO:  second	6.789001
+INFO:  year	1
+-- errors (not worth testing many combinations, they all share a code path)
+do language pllua $$ print(pgtype.time('03:45:01.234567').dow) $$;
+ERROR:  unit "dow" not supported for type time without time zone
+--end
--- /dev/null
+++ b/expected/spi_1.out
@@ -0,0 +1,262 @@
+--
+\set VERBOSITY terse
+--
+-- Test of SPI-related functionality.
+create temp table tsttab (
+  id integer primary key,
+  a integer,
+  b text,
+  c numeric,
+  d date
+);
+insert into tsttab(id, a,b,c,d)
+  values (1, 1,'foo',2.34,'2017-01-01'),
+  	 (2, 2,'bar',2.34,'2017-02-01'),
+  	 (3, 3,'baz',2.34,'2017-03-01'),
+	 (4, 4, 'fred',2.34,'2017-04-01'),
+	 (5, 5,'jim',2.34,'2017-05-01'),
+	 (6, 6,'sheila',2.34,'2017-06-01');
+-- basics
+do language pllua $$
+  local tbl
+  tbl = spi.execute([[ select 1 as a, 'foo'::text as b ]])
+  print(#tbl,tbl[1],type(tbl[1]))
+  print(tbl[1].a,tbl[1].b)
+  tbl = spi.execute([[ select i, 'foo'::text as b from generate_series(1,10000) i ]])
+  print(#tbl,tbl[1],tbl[10000])
+  tbl = spi.execute([[ select * from tsttab order by id ]])
+  for i = 1,#tbl do print(tbl[i]) end
+$$;
+INFO:  1	(1,foo)	userdata
+INFO:  1	foo
+INFO:  10000	(1,foo)	(10000,foo)
+INFO:  (1,1,foo,2.34,01-01-2017)
+INFO:  (2,2,bar,2.34,02-01-2017)
+INFO:  (3,3,baz,2.34,03-01-2017)
+INFO:  (4,4,fred,2.34,04-01-2017)
+INFO:  (5,5,jim,2.34,05-01-2017)
+INFO:  (6,6,sheila,2.34,06-01-2017)
+-- statements
+do language pllua $$
+  local stmt,tbl
+  stmt = spi.prepare([[ select * from tsttab where id=$1 ]], {"integer"})
+  tbl = stmt:execute(1)
+  print(tbl[1])
+  -- __call metamethod
+  tbl = stmt(6)
+  print(tbl[1])
+  stmt = spi.prepare([[ select * from tsttab where id = ANY ($1) order by id ]],
+                     {pgtype.array.integer})
+  tbl = stmt:execute(pgtype.array.integer(1,nil,3))
+  print(#tbl,tbl[1],tbl[2])
+  -- type deduction:
+  stmt = spi.prepare([[ select 1 + $1 as a, pg_typeof($1) ]])
+  tbl = stmt:execute(1)
+  print(#tbl,tbl[1])
+$$;
+INFO:  (1,1,foo,2.34,01-01-2017)
+INFO:  (6,6,sheila,2.34,06-01-2017)
+INFO:  2	(1,1,foo,2.34,01-01-2017)	(3,3,baz,2.34,03-01-2017)
+INFO:  1	(2,integer)
+-- iterators
+do language pllua $$
+  for r in spi.rows([[ select * from tsttab order by id ]]) do
+    print(r)
+  end
+  stmt = spi.prepare([[ select * from tsttab where id = ANY ($1) ]],
+                     {pgtype.array.integer})
+  for r in stmt:rows(pgtype.array.integer(1,nil,3)) do
+    print(r)
+  end
+$$;
+INFO:  (1,1,foo,2.34,01-01-2017)
+INFO:  (2,2,bar,2.34,02-01-2017)
+INFO:  (3,3,baz,2.34,03-01-2017)
+INFO:  (4,4,fred,2.34,04-01-2017)
+INFO:  (5,5,jim,2.34,05-01-2017)
+INFO:  (6,6,sheila,2.34,06-01-2017)
+INFO:  (1,1,foo,2.34,01-01-2017)
+INFO:  (3,3,baz,2.34,03-01-2017)
+do language pllua $$
+  local c = spi.newcursor('curs1')
+  local stmt = spi.prepare([[ select * from tsttab order by id for update ]])
+  c:open(stmt)
+  for r in c:rows() do
+    print(r)
+    if r.id == 3 then
+      spi.execute([[ update tsttab set c = c + 10 where current of curs1 ]])
+    end
+  end
+  c:move(0, 'absolute')
+  for r in c:rows() do
+    print(r)
+  end
+  for r in spi.rows([[ select * from tsttab order by id ]]) do
+    print(r)
+  end
+  spi.execute([[ update tsttab set c = c - 10 where id=3 ]])
+  c:close()
+$$;
+INFO:  (1,1,foo,2.34,01-01-2017)
+INFO:  (2,2,bar,2.34,02-01-2017)
+INFO:  (3,3,baz,2.34,03-01-2017)
+INFO:  (4,4,fred,2.34,04-01-2017)
+INFO:  (5,5,jim,2.34,05-01-2017)
+INFO:  (6,6,sheila,2.34,06-01-2017)
+ERROR:  cursor can only scan forward
+-- cursors
+begin;
+declare foo scroll cursor for select * from tsttab order by id;
+do language pllua $$
+  local c = spi.findcursor("foo")
+  local tbl
+  tbl = c:fetch(1,'next')
+  print(#tbl,tbl[1])
+  tbl = c:fetch(2,'forward')  -- same as 'next'
+  print(#tbl,tbl[1],tbl[2])
+  tbl = c:fetch(1,'absolute')
+  print(#tbl,tbl[1])
+  tbl = c:fetch(4,'relative')
+  print(#tbl,tbl[1])
+  tbl = c:fetch(1,'prior')    -- same as 'backward'
+  print(#tbl,tbl[1])
+  tbl = c:fetch(1,'backward')
+  print(#tbl,tbl[1])
+  print(c:isopen())
+  spi.execute("close foo")
+  print(c:isopen())
+$$;
+INFO:  1	(1,1,foo,2.34,01-01-2017)
+INFO:  2	(2,2,bar,2.34,02-01-2017)	(3,3,baz,2.34,03-01-2017)
+INFO:  1	(1,1,foo,2.34,01-01-2017)
+INFO:  1	(5,5,jim,2.34,05-01-2017)
+INFO:  1	(4,4,fred,2.34,04-01-2017)
+INFO:  1	(3,3,baz,2.34,03-01-2017)
+INFO:  true
+INFO:  false
+commit;
+do language pllua $$
+  local c = spi.newcursor("bar")
+  c:open([[ select * from tsttab where id >= $1 order by id ]], 3)
+  local tbl
+  tbl = c:fetch(1,'next')
+  print(#tbl,tbl[1])
+  for k,v in ipairs(spi.execute([[ select name, statement from pg_cursors ]])) do
+    print(v.name, v.statement)
+  end
+  c:close()
+  for k,v in ipairs(spi.execute([[ select name, statement from pg_cursors ]])) do
+    print(v.name, v.statement)
+  end
+  c:open([[ select * from tsttab where id < $1 order by id desc ]], 3)
+  tbl = c:fetch(3,'next')
+  print(#tbl,tbl[1],tbl[2])
+  for k,v in ipairs(spi.execute([[ select name, statement from pg_cursors ]])) do
+    print(v.name, v.statement)
+  end
+  c:close()
+$$;
+INFO:  1	(3,3,baz,2.34,03-01-2017)
+INFO:  bar	 select * from tsttab where id >= $1 order by id 
+INFO:  2	(2,2,bar,2.34,02-01-2017)	(1,1,foo,2.34,01-01-2017)
+INFO:  bar	 select * from tsttab where id < $1 order by id desc 
+-- cursor options on statement
+do language pllua $$
+  local stmt = spi.prepare([[ select * from tsttab where id >= $1 order by id ]],
+                           {"integer"}, { scroll = true, fast_start = true, generic_plan = true })
+  local stmt2 = spi.prepare([[ select * from tsttab where id >= $1 order by id ]],
+                            {"integer"}, { no_scroll = true })
+  local c = stmt:getcursor(4)
+  local tbl
+  tbl = c:fetch(3,'next')
+  print(#tbl,tbl[1],tbl[2])
+  c:move(0,'absolute')
+  tbl = c:fetch(3,'next')
+  print(#tbl,tbl[1],tbl[2])
+  for k,v in ipairs(spi.execute([[ select name, statement, is_scrollable from pg_cursors ]])) do
+    print(v.name, v.statement, v.is_scrollable)
+  end
+  c:close()
+  c = stmt2:getcursor(4)
+  local c2 = spi.findcursor(c:name())
+  print(c:name(), rawequal(c,c2))
+  for k,v in ipairs(spi.execute([[ select name, statement, is_scrollable from pg_cursors ]])) do
+    print(v.name, v.statement, v.is_scrollable)
+  end
+  c:close()
+$$;
+INFO:  3	(4,4,fred,2.34,04-01-2017)	(5,5,jim,2.34,05-01-2017)
+INFO:  3	(4,4,fred,2.34,04-01-2017)	(5,5,jim,2.34,05-01-2017)
+INFO:  <unnamed portal 3>	 select * from tsttab where id >= $1 order by id 	true
+INFO:  <unnamed portal 4>	true
+INFO:  <unnamed portal 4>	 select * from tsttab where id >= $1 order by id 	false
+-- check missing params are OK
+do language pllua $$
+  local stmt = spi.prepare([[ select * from generate_series($1::integer, $3) i ]]);
+  print(stmt:argtype(1):name())
+  print(type(stmt:argtype(2)))
+  print(stmt:argtype(3):name())
+$$;
+INFO:  integer
+INFO:  nil
+INFO:  integer
+-- check execute_count
+do language pllua $$
+  local q = [[ select * from generate_series($1::integer,$2) i ]]
+  local r1 = spi.execute_count(q, 2, 1, 5)
+  print(#r1)
+  local s = spi.prepare(q, {"integer","integer"})
+  r1 = s:execute_count(3,1,5)
+  print(#r1)
+$$;
+INFO:  2
+INFO:  3
+-- cursors as parameters and return values
+create function do_fetch(c refcursor) returns void language pllua as $$
+  while true do
+    local r = (c:fetch())[1]
+    if r==nil then break end
+    print(r)
+  end
+$$;
+create function do_exec(q text, n text) returns refcursor language pllua as $$
+  local s = spi.prepare(q)
+  local c = spi.newcursor(n)
+  return c:open(s):disown()
+$$;
+begin;
+declare mycur cursor for select * from tsttab order by id;
+select do_fetch('mycur');
+INFO:  (1,1,foo,2.34,01-01-2017)
+INFO:  (2,2,bar,2.34,02-01-2017)
+INFO:  (3,3,baz,2.34,03-01-2017)
+INFO:  (4,4,fred,2.34,04-01-2017)
+INFO:  (5,5,jim,2.34,05-01-2017)
+INFO:  (6,6,sheila,2.34,06-01-2017)
+ do_fetch 
+----------
+ 
+(1 row)
+
+commit;
+begin;
+select do_exec('select * from tsttab order by id desc', 'mycur2');
+ do_exec 
+---------
+ mycur2
+(1 row)
+
+do language pllua $$ collectgarbage() $$;  -- check cursor stays open
+fetch all from mycur2;
+ id | a |   b    |  c   |     d      
+----+---+--------+------+------------
+  6 | 6 | sheila | 2.34 | 06-01-2017
+  5 | 5 | jim    | 2.34 | 05-01-2017
+  4 | 4 | fred   | 2.34 | 04-01-2017
+  3 | 3 | baz    | 2.34 | 03-01-2017
+  2 | 2 | bar    | 2.34 | 02-01-2017
+  1 | 1 | foo    | 2.34 | 01-01-2017
+(6 rows)
+
+commit;
+--end
