// Copyright 2023 Dolthub, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package _go import ( "testing" "github.com/dolthub/go-mysql-server/sql" ) func TestAggregateFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "bool_and", SetUpScript: []string{ `CREATE TABLE t1 (pk INT primary key, v1 BOOLEAN, v2 BOOLEAN);`, `INSERT INTO t1 VALUES (1, true, false), (2, true, true), (3, true, true);`, `CREATE TABLE t2 (v1 BOOLEAN);`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT bool_and(v1), bool_and(v2) FROM t1;`, Expected: []sql.Row{{"t", "f"}}, }, { Query: `SELECT bool_and(v1 and v2) FROM t1;`, Expected: []sql.Row{ {"f"}, }, }, { Query: `SELECT bool_and(v1 and v2) FROM t1 where v1 and v2;`, Expected: []sql.Row{ {"t"}, }, }, { Query: `SELECT bool_and(v1) FROM t1 where pk > 10;`, Expected: []sql.Row{ {nil}, }, }, { Skip: true, // building a values-derived table's type fails here, postgres is more permissive Query: `SELECT bool_and(a) FROM (VALUES(true),(false),(null)) r(a);`, Expected: []sql.Row{ {"f"}, }, }, { Query: `SELECT bool_and(a) FROM (VALUES(true),(false),(null::bool)) r(a);`, Expected: []sql.Row{ {"f"}, }, }, { Query: `SELECT bool_and(a) FROM (VALUES(null::bool),(true),(null::bool)) r(a);`, Expected: []sql.Row{ {"t"}, }, }, { Query: `SELECT bool_and(v1) FROM t2`, Expected: []sql.Row{ {nil}, }, }, }, }, { Name: "bool_or", SetUpScript: []string{ `CREATE TABLE t1 (pk INT primary key, v1 BOOLEAN, v2 BOOLEAN);`, `INSERT INTO t1 VALUES (1, false, false), (2, true, true), (3, true, false);`, `CREATE TABLE t2 (v1 BOOLEAN);`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT bool_or(v1), bool_or(v2) FROM t1;`, Expected: []sql.Row{{"t", "t"}}, }, { Query: `SELECT bool_or(v1), bool_or(v2) FROM t1 where pk <> 2;`, Expected: []sql.Row{{"t", "f"}}, }, { Query: `SELECT bool_or(v1 and v2) FROM t1;`, Expected: []sql.Row{ {"t"}, }, }, { Query: `SELECT bool_or(v1) FROM t1 where pk > 10;`, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT bool_or(a) FROM (VALUES(true),(false),(null::bool)) r(a);`, Expected: []sql.Row{ {"t"}, }, }, { Query: `SELECT bool_or(a) FROM (VALUES(null::bool),(false),(null::bool)) r(a);`, Expected: []sql.Row{ {"f"}, }, }, { Query: `SELECT bool_or(v1) FROM t2`, Expected: []sql.Row{ {nil}, }, }, }, }, { Name: "array_agg", SetUpScript: []string{ `CREATE TABLE t1 (pk INT primary key, t timestamp, v varchar, f float[]);`, `INSERT INTO t1 VALUES (1, '2023-01-01 00:00:00', 'a', '{1.0, 2.0}'), (2, '2023-01-02 00:00:00', 'b', '{3.0, 4.0}'), (3, '2023-01-03 00:00:00', 'c', '{5.0, 6.0}');`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT array_agg(pk) FROM t1;`, Expected: []sql.Row{ {"{1,2,3}"}, }, }, { Query: `SELECT array_agg(t) FROM t1;`, Expected: []sql.Row{ {`{"2023-01-01 00:00:00","2023-01-02 00:00:00","2023-01-03 00:00:00"}`}, }, }, { Query: `SELECT array_agg(v) FROM t1;`, Expected: []sql.Row{ {"{a,b,c}"}, }, }, { Skip: true, // Higher-level arrays don't work because they panic during output Query: `SELECT array_agg(f) FROM t1;`, Expected: []sql.Row{ {"{{1.0,2.0},{3.0,4.0},{5.0,6.0}}"}, }, }, }, }, { Name: "array_agg with order by", SetUpScript: []string{ `CREATE TABLE test_data ( id INT PRIMARY KEY, name VARCHAR(50), age INT, score FLOAT, created_at TIMESTAMP, category CHAR(1), nullable_field VARCHAR(20) );`, `INSERT INTO test_data VALUES (1, 'Alice', 25, 95.5, '2023-01-03 10:00:00', 'A', 'value1'), (2, 'Bob', 30, 87.2, '2023-01-01 09:30:00', 'B', NULL), (3, 'Charlie', 22, 92.8, '2023-01-02 11:15:00', 'A', 'value2'), (4, 'Diana', 28, 88.9, '2023-01-04 08:45:00', 'C', NULL), (5, 'Eve', 35, 94.1, '2023-01-05 14:20:00', 'B', 'value3'), (6, 'Frank', 26, 89.3, '2023-01-06 16:30:00', 'A', 'value4');`, }, Assertions: []ScriptTestAssertion{ // Basic ORDER BY ASC { Query: `SELECT array_agg(name ORDER BY age ASC) FROM test_data;`, Expected: []sql.Row{ {"{Charlie,Alice,Frank,Diana,Bob,Eve}"}, }, }, // Basic ORDER BY DESC { Query: `SELECT array_agg(name ORDER BY age DESC) FROM test_data;`, Expected: []sql.Row{ {"{Eve,Bob,Diana,Frank,Alice,Charlie}"}, }, }, // ORDER BY with integers { Query: `SELECT array_agg(id ORDER BY age) FROM test_data;`, Expected: []sql.Row{ {"{3,1,6,4,2,5}"}, }, }, // ORDER BY with floats { Query: `SELECT array_agg(name ORDER BY score DESC) FROM test_data;`, Expected: []sql.Row{ {"{Alice,Eve,Charlie,Frank,Diana,Bob}"}, }, }, // ORDER BY with timestamps { Query: `SELECT array_agg(name ORDER BY created_at ASC) FROM test_data;`, Expected: []sql.Row{ {"{Bob,Charlie,Alice,Diana,Eve,Frank}"}, }, }, // ORDER BY with VARCHAR/CHAR { Query: `SELECT array_agg(age ORDER BY name) FROM test_data;`, Expected: []sql.Row{ {"{25,30,22,28,35,26}"}, }, }, // Multiple columns in ORDER BY { Query: `SELECT array_agg(name ORDER BY category ASC, age DESC) FROM test_data;`, Expected: []sql.Row{ {"{Frank,Alice,Charlie,Eve,Bob,Diana}"}, }, }, // ORDER BY with mixed ASC/DESC { Query: `SELECT array_agg(id ORDER BY category ASC, score DESC) FROM test_data;`, Expected: []sql.Row{ {"{1,3,6,5,2,4}"}, }, }, // ORDER BY with expression { Query: `SELECT array_agg(name ORDER BY age * 2) FROM test_data;`, Expected: []sql.Row{ {"{Charlie,Alice,Frank,Diana,Bob,Eve}"}, }, }, // ORDER BY with string concatenation { Query: `SELECT array_agg(age ORDER BY category || name) FROM test_data;`, Expected: []sql.Row{ {"{25,22,26,30,35,28}"}, }, }, // ORDER BY with CASE expression { Query: `SELECT array_agg(name ORDER BY CASE WHEN age > 27 THEN 1 ELSE 0 END, age) FROM test_data;`, Expected: []sql.Row{ {"{Charlie,Alice,Frank,Diana,Bob,Eve}"}, }, }, // ORDER BY with NULL values (NULLS FIRST behavior) { Query: `SELECT array_agg(name ORDER BY nullable_field) FROM test_data;`, Expected: []sql.Row{ {"{Bob,Diana,Alice,Charlie,Eve,Frank}"}, }, }, // ORDER BY with GROUP BY { Query: `SELECT category, array_agg(name ORDER BY age) FROM test_data GROUP BY category ORDER BY category;`, Expected: []sql.Row{ {"A", "{Charlie,Alice,Frank}"}, {"B", "{Bob,Eve}"}, {"C", "{Diana}"}, }, }, // ORDER BY with subquery correlation { Query: `SELECT category, array_agg(name ORDER BY (SELECT COUNT(*) FROM test_data t2 WHERE t2.category = test_data.category AND t2.age < test_data.age)) FROM test_data GROUP BY category ORDER BY category;`, Expected: []sql.Row{ {"A", "{Charlie,Alice,Frank}"}, {"B", "{Bob,Eve}"}, {"C", "{Diana}"}, }, }, // ORDER BY with COALESCE for NULL handling { Query: `SELECT array_agg(name ORDER BY COALESCE(nullable_field, 'zzz')) FROM test_data;`, Expected: []sql.Row{ {"{Alice,Charlie,Eve,Frank,Bob,Diana}"}, }, }, // Complex ORDER BY with multiple expressions { Query: `SELECT array_agg(name ORDER BY LENGTH(name) DESC, name ASC) FROM test_data;`, Expected: []sql.Row{ {"{Charlie,Alice,Diana,Frank,Bob,Eve}"}, }, }, // ORDER BY with aggregated values in grouped context { Query: `SELECT category, array_agg(name ORDER BY score - (SELECT AVG(score) FROM test_data t2 WHERE t2.category = test_data.category)) FROM test_data GROUP BY category ORDER BY category;`, Expected: []sql.Row{ {"A", "{Frank,Charlie,Alice}"}, {"B", "{Bob,Eve}"}, {"C", "{Diana}"}, }, }, // ORDER BY with date functions { Query: `SELECT array_agg(name ORDER BY EXTRACT(hour FROM created_at)) FROM test_data;`, Expected: []sql.Row{ {"{Diana,Bob,Alice,Charlie,Eve,Frank}"}, }, }, // Empty result set { Query: `SELECT array_agg(name ORDER BY age) FROM test_data WHERE age > 100;`, Expected: []sql.Row{ {nil}, }, }, // ORDER BY with boolean expression { Query: `SELECT array_agg(name ORDER BY age > 27, age) FROM test_data;`, Expected: []sql.Row{ {"{Charlie,Alice,Frank,Diana,Bob,Eve}"}, }, }, }, }, { Name: "array agg with case statement", SetUpScript: []string{ "CREATE TABLE t1 (pk INT primary key, v1 INT, v2 INT);", "INSERT INTO t1 VALUES (1, 10, 20), (2, 30, 40), (3, 50, 60);", "CREATE TABLE t2 (pk INT primary key, v1 INT, v2 TEXT);", "INSERT INTO t2 VALUES (1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c');", }, Assertions: []ScriptTestAssertion{ { Query: `SELECT array_agg(CASE WHEN v1 > 20 THEN v1 ELSE NULL END) FROM t1;`, Expected: []sql.Row{ {"{NULL,30,50}"}, }, }, { Query: `SELECT array_agg(CASE WHEN v1 >= 20 THEN v2 ELSE NULL END) FROM t2;`, Expected: []sql.Row{ {"{NULL,b,c}"}, }, }, { Query: `SELECT array_agg(CASE WHEN v1 > 20 THEN v1::text ELSE v2 END) FROM t2;`, Expected: []sql.Row{ {"{a,b,30}"}, }, }, { // Panic on type mixing, the logic for mixed types is hard-coded in GMS plan builder, needs // to be configurable. Postgres rejects this plan because of the type differences Skip: true, Query: `SELECT array_agg(CASE WHEN v1 > 20 THEN v1 ELSE v2 END) FROM t2;`, Expected: []sql.Row{ {"{a,b,30}"}, }, }, }, }, { Name: "array_agg with DISTINCT", SetUpScript: []string{ `CREATE TABLE test_items ( id INT PRIMARY KEY, category TEXT NOT NULL, name TEXT NOT NULL );`, `INSERT INTO test_items (id, category, name) VALUES (1, 'A', 'foo'), (2, 'A', 'bar'), (3, 'B', 'baz'), (4, 'A', 'foo');`, }, Assertions: []ScriptTestAssertion{ { // Order is non-deterministic without ORDER BY; value reflects insertion-order of first occurrences Query: `SELECT array_agg(DISTINCT name) FROM test_items;`, Expected: []sql.Row{ {"{foo,bar,baz}"}, }, }, { // https://github.com/dolthub/doltgresql/issues/2334 Query: `SELECT array_agg(DISTINCT name ORDER BY name ASC) FROM test_items;`, Expected: []sql.Row{ {"{bar,baz,foo}"}, }, }, { Query: `SELECT array_agg(DISTINCT name ORDER BY name DESC) FROM test_items;`, Expected: []sql.Row{ {"{foo,baz,bar}"}, }, }, { Query: `SELECT category, array_agg(DISTINCT name ORDER BY name ASC) FROM test_items GROUP BY category ORDER BY category;`, Expected: []sql.Row{ {"A", "{bar,foo}"}, {"B", "{baz}"}, }, }, { Query: `SELECT array_agg(DISTINCT category ORDER BY category) FROM test_items;`, Expected: []sql.Row{ {"{A,B}"}, }, }, }, }, { Name: "array_agg with DISTINCT and composite types", SetUpScript: []string{ `CREATE TYPE point_t AS (x INT, y INT);`, `CREATE TABLE points (id INT PRIMARY KEY, p point_t);`, `INSERT INTO points VALUES (1, ROW(1, 2)), (2, ROW(3, 4)), (3, ROW(1, 2)), (4, ROW(5, 6));`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT array_agg(DISTINCT p ORDER BY p) FROM points;`, Expected: []sql.Row{ {`{"(1,2)","(3,4)","(5,6)"}`}, }, }, }, }, { Name: "array_agg with DISTINCT handles NULL values", SetUpScript: []string{ "CREATE TABLE t (v text);", "INSERT INTO t VALUES (NULL), (NULL), ('x');", }, Assertions: []ScriptTestAssertion{ { Query: "SELECT array_agg(DISTINCT v ORDER BY v NULLS FIRST)::text FROM t;", Expected: []sql.Row{ {"{NULL,x}"}, }, }, { Query: "SELECT array_agg(DISTINCT v)::text FROM (VALUES (NULL::text), (NULL::text)) AS vals(v);", Expected: []sql.Row{ {"{NULL}"}, }, }, }, }, { Name: "array_agg DISTINCT dedups semantically equal numerics", Assertions: []ScriptTestAssertion{ { Query: "SELECT array_agg(DISTINCT v ORDER BY v)::text FROM (VALUES (1.0::numeric), (1.00::numeric)) AS vals(v);", Expected: []sql.Row{ {"{1.0}"}, }, }, { Query: ` SELECT array_agg(DISTINCT v ORDER BY v)::text FROM ( VALUES (1::numeric), (1.000000::numeric) ) AS vals(v); `, Expected: []sql.Row{ {"{1}"}, }, }, }, }, }) } // https://www.postgresql.org/docs/15/functions-math.html func TestFunctionsMath(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "cbrt", SetUpScript: []string{ `CREATE TABLE test (pk INT primary key, v1 INT, v2 FLOAT4, v3 FLOAT8, v4 VARCHAR(255));`, `INSERT INTO test VALUES (1, -1, -2, -3, '-5'), (2, 7, 11, 13, '17'), (3, 19, -23, 29, '-31');`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT cbrt(v1), cbrt(v2), cbrt(v3) FROM test ORDER BY pk;`, Skip: true, // Our values are slightly different Expected: []sql.Row{ {-1.0, -1.259921049894873, -1.4422495703074083}, {1.9129311827723892, 2.2239800905693157, 2.3513346877207573}, {2.668401648721945, -2.8438669798515654, 3.0723168256858475}, }, }, { Query: `SELECT round(cbrt(v1)::numeric, 10), round(cbrt(v2)::numeric, 10), round(cbrt(v3)::numeric, 10) FROM test ORDER BY pk;`, ExpectedColNames: []string{"round", "round", "round"}, Expected: []sql.Row{ {Numeric("-1.0000000000"), Numeric("-1.2599210499"), Numeric("-1.4422495703")}, {Numeric("1.9129311828"), Numeric("2.2239800906"), Numeric("2.3513346877")}, {Numeric("2.6684016487"), Numeric("-2.8438669799"), Numeric("3.0723168257")}, }, }, { Query: `SELECT cbrt(v4) FROM test ORDER BY pk;`, ExpectedErr: "function cbrt(varchar(255)) does not exist", }, { Query: `SELECT cbrt('64');`, ExpectedColNames: []string{"cbrt"}, Expected: []sql.Row{ {4.0}, }, }, { Query: `SELECT round(cbrt('64'));`, ExpectedColNames: []string{"round"}, Expected: []sql.Row{ {4.0}, }, }, { Query: `SELECT round('NaN'::numeric);`, ExpectedColNames: []string{"round"}, Expected: []sql.Row{{Numeric("NaN")}}, }, { Query: `SELECT 0::numeric::float8;`, ExpectedColNames: []string{"float8"}, Expected: []sql.Row{{0.0}}, }, }, }, { Name: "gcd", SetUpScript: []string{ `CREATE TABLE test (pk INT primary key, v1 INT4, v2 INT8, v3 FLOAT8, v4 VARCHAR(255));`, `INSERT INTO test VALUES (1, -2, -4, -6, '-8'), (2, 10, 12, 14.14, '16.16'), (3, 18, -20, 22.22, '-24.24');`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT gcd(v1, 10), gcd(v2, 20) FROM test ORDER BY pk;`, ExpectedColNames: []string{"gcd", "gcd"}, Expected: []sql.Row{ {2, 4}, {10, 4}, {2, 20}, }, }, { Query: `SELECT gcd(v3, 10) FROM test ORDER BY pk;`, ExpectedErr: "function gcd(double precision, integer)", }, { Query: `SELECT gcd(v4, 10) FROM test ORDER BY pk;`, ExpectedErr: "function gcd(varchar(255), integer) does not exist", }, { Query: `SELECT gcd(36, '48');`, ExpectedColNames: []string{"gcd"}, Expected: []sql.Row{ {12}, }, }, { Query: `SELECT gcd('36', 48);`, ExpectedColNames: []string{"gcd"}, Expected: []sql.Row{ {12}, }, }, { Query: `SELECT gcd(1, 0), gcd(0, 1), gcd(0, 0);`, ExpectedColNames: []string{"gcd", "gcd", "gcd"}, Expected: []sql.Row{ {1, 1, 0}, }, }, }, }, { Name: "lcm", SetUpScript: []string{ `CREATE TABLE test (pk INT primary key, v1 INT4, v2 INT8, v3 FLOAT8, v4 VARCHAR(255));`, `INSERT INTO test VALUES (1, -2, -4, -6, '-8'), (2, 10, 12, 14.14, '16.16'), (3, 18, -20, 22.22, '-24.24');`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT lcm(v1, 10), lcm(v2, 20) FROM test ORDER BY pk;`, ExpectedColNames: []string{"lcm", "lcm"}, Expected: []sql.Row{ {10, 20}, {10, 60}, {90, 20}, }, }, { Query: `SELECT lcm(v3, 10) FROM test ORDER BY pk;`, ExpectedErr: "function lcm(double precision, integer)", }, { Query: `SELECT lcm(v4, 10) FROM test ORDER BY pk;`, ExpectedErr: "function lcm(varchar(255), integer) does not exist", }, { Query: `SELECT lcm(36, '48');`, Expected: []sql.Row{ {144}, }, }, { Query: `SELECT lcm('36', 48);`, Expected: []sql.Row{ {144}, }, }, { Query: `SELECT lcm(1, 0), lcm(0, 1), lcm(0, 0);`, Expected: []sql.Row{ {0, 0, 0}, }, }, }, }, { Name: "power", Assertions: []ScriptTestAssertion{ { Query: `SELECT power(1::float8, 1::float8);`, Expected: []sql.Row{ {1.0}, }, }, { Query: `SELECT power(2::float8, 0.5::float8);`, Expected: []sql.Row{ {1.4142135623730951}, }, }, { Query: `SELECT power(0::float8, 0::float8);`, Expected: []sql.Row{ {1.0}, }, }, { Query: `SELECT power(4::float8, -1::float8);`, Expected: []sql.Row{ {0.25}, }, }, { Query: `SELECT power(-2::float8, -1::float8);`, Expected: []sql.Row{ {-0.5}, }, }, { Query: `SELECT power(0::float8, -1::float8);`, ExpectedErr: `zero raised to a negative power is undefined`, }, { Query: `SELECT power(1::numeric, 1::numeric)::float8;`, Expected: []sql.Row{ {1.0}, }, }, { Query: `SELECT power(2::numeric, 0.5::numeric)::float8;`, Skip: true, // TODO: we don't handle non-integer exponents properly Expected: []sql.Row{ {1.4142135623730951}, }, }, { Query: `SELECT power(0::numeric, 0::numeric)::float8;`, Expected: []sql.Row{ {1.0}, }, }, { Query: `SELECT power(4::numeric, -1::numeric)::float8;`, Expected: []sql.Row{ {0.25}, }, }, { Query: `SELECT power(-2::numeric, -1::numeric)::float8;`, Expected: []sql.Row{ {-0.5}, }, }, { Query: `SELECT power(0::numeric, -1::numeric);`, ExpectedErr: `zero raised to a negative power is undefined`, }, { Query: `select power('3'::numeric, '-1'::numeric);`, Expected: []sql.Row{ {Numeric("0.3333333333333333")}, }, }, { Query: `select power('3'::numeric, '-3'::numeric);`, Expected: []sql.Row{ {Numeric("0.0370370370370370")}, }, }, { Query: `select power('Nan'::numeric, '-3'::numeric);`, Expected: []sql.Row{ {Numeric("NaN")}, }, }, { Query: `select power('inf'::numeric, '-3'::numeric);`, Expected: []sql.Row{ {Numeric("0")}, }, }, { Query: `select power('-inf'::numeric, '-3'::numeric);`, Expected: []sql.Row{ {Numeric("0")}, }, }, { Query: `select power('-inf'::numeric, '3'::numeric);`, Expected: []sql.Row{ {Numeric("-Infinity")}, }, }, { Query: `select power('-inf'::numeric, '4'::numeric);`, Expected: []sql.Row{ {Numeric("Infinity")}, }, }, { Query: `select power('0'::numeric, '3'::numeric);`, Expected: []sql.Row{ {Numeric("0.0000000000000000")}, }, }, { Query: `select power('-inf'::numeric, '-inf'::numeric);`, Expected: []sql.Row{ {Numeric("0")}, }, }, { Query: `select power('-inf'::numeric, 'inf'::numeric);`, Expected: []sql.Row{ {Numeric("Infinity")}, }, }, { Query: `select power('-inf'::numeric, 'nan'::numeric);`, Expected: []sql.Row{ {Numeric("NaN")}, }, }, }, }, }) } func TestFunctionsOID(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "to_regclass", SetUpScript: []string{ `CREATE TABLE testing (pk INT primary key, v1 INT UNIQUE);`, `CREATE TABLE "Testing2" (pk INT primary key, v1 INT);`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT to_regclass('testing');`, ExpectedColNames: []string{"to_regclass"}, Expected: []sql.Row{ {"testing"}, }, }, { Query: `SELECT to_regclass('Testing2');`, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT to_regclass('"Testing2"');`, Expected: []sql.Row{ {"Testing2"}, }, }, { Query: `SELECT to_regclass(('testing'::regclass)::text);`, Expected: []sql.Row{ {"testing"}, }, }, { Query: `SELECT to_regclass((('testing'::regclass)::oid)::text);`, Expected: []sql.Row{ {nil}, }, }, { // When the relation is from a schema on the search path, it is not qualified with the schema name Query: `SELECT to_regclass(('public.testing'::regclass)::text);`, Expected: []sql.Row{ {"testing"}, }, }, { // Clear out the current search_path setting to test fully qualified relation names Query: `SET search_path = '';`, Expected: []sql.Row{}, }, { Query: `SELECT to_regclass(('public.testing'::regclass)::text);`, Expected: []sql.Row{ {"public.testing"}, }, }, }, }, { Name: "to_regproc", Assertions: []ScriptTestAssertion{ { Query: `SELECT to_regproc('acos');`, ExpectedColNames: []string{"to_regproc"}, Expected: []sql.Row{ {"acos"}, }, }, { Query: `SELECT to_regproc('acos"');`, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT to_regproc(('acos'::regproc)::text);`, Expected: []sql.Row{ {"acos"}, }, }, { Query: `SELECT to_regproc((('acos'::regproc)::oid)::text);`, Expected: []sql.Row{ {nil}, }, }, }, }, { Name: "to_regtype", Assertions: []ScriptTestAssertion{ { Query: `SELECT to_regtype('integer');`, ExpectedColNames: []string{"to_regtype"}, Expected: []sql.Row{ {"integer"}, }, }, { Query: `SELECT to_regtype('integer[]');`, Expected: []sql.Row{ {"integer[]"}, }, }, { Query: `SELECT to_regtype('int4');`, Expected: []sql.Row{ {"integer"}, }, }, { Query: `SELECT to_regtype('varchar');`, Expected: []sql.Row{ {"character varying"}, }, }, { Query: `SELECT to_regtype('pg_catalog.varchar');`, Expected: []sql.Row{ {"character varying"}, }, }, { Query: `SELECT to_regtype('varchar(10)');`, Expected: []sql.Row{ {"character varying"}, }, }, { Query: `SELECT to_regtype('char');`, Expected: []sql.Row{ {"character"}, }, }, { Query: `SELECT to_regtype('pg_catalog.char');`, Expected: []sql.Row{ {`"char"`}, }, }, { Query: `SELECT to_regtype('char(10)');`, Expected: []sql.Row{ {"character"}, }, }, { Query: `SELECT to_regtype('"char"');`, Expected: []sql.Row{ {`"char"`}, }, }, { Query: `SELECT to_regtype('pg_catalog."char"');`, Expected: []sql.Row{ {`"char"`}, }, }, { Query: `SELECT to_regtype('otherschema.char');`, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT to_regtype('timestamp');`, Expected: []sql.Row{ {"timestamp without time zone"}, }, }, { Query: `SELECT to_regtype('timestamp without time zone');`, Expected: []sql.Row{ {"timestamp without time zone"}, }, }, { Query: `SELECT to_regtype('integer"');`, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT to_regtype(('integer'::regtype)::text);`, Expected: []sql.Row{ {"integer"}, }, }, { Query: `SELECT to_regtype(('int'::regtype)::text);`, Expected: []sql.Row{ {"integer"}, }, }, { Query: `SELECT to_regtype((('integer'::regtype)::oid)::text);`, Expected: []sql.Row{ {nil}, }, }, }, }, }) } func TestSystemInformationFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "current_database", Database: "test", Assertions: []ScriptTestAssertion{ { Query: `SELECT current_database();`, ExpectedColNames: []string{"current_database"}, Expected: []sql.Row{ {"test"}, }, }, { Query: `SELECT current_database;`, ExpectedErr: `column "current_database" could not be found in any table in scope`, }, // TODO: Implement table function for current_database { Query: `SELECT * FROM current_database();`, Skip: true, Expected: []sql.Row{ {"test"}, }, }, { Query: `SELECT * FROM current_database;`, ExpectedErr: "table not found: current_database", }, }, }, { Name: "current_catalog", Database: "test", Assertions: []ScriptTestAssertion{ { Skip: true, // TODO: current_catalog currently returns current_database column name Query: `SELECT current_catalog;`, ExpectedColNames: []string{"current_catalog"}, Expected: []sql.Row{ {"test"}, }, }, { Query: `SELECT current_catalog;`, Expected: []sql.Row{ {"test"}, }, }, { Query: `SELECT current_catalog();`, ExpectedErr: `ERROR: at or near "(": syntax error (SQLSTATE XX000)`, }, // // TODO: Implement table function for current_catalog { Query: `SELECT * FROM current_catalog;`, Skip: true, Expected: []sql.Row{ {"test"}, }, }, { Query: `SELECT * FROM current_catalog();`, ExpectedErr: `ERROR: at or near "(": syntax error (SQLSTATE XX000)`, }, }, }, { Name: "current_schema", Assertions: []ScriptTestAssertion{ { Skip: true, // TODO: current_schema currently returns column name in quotes Query: `SELECT current_schema();`, ExpectedColNames: []string{"\"current_schema\""}, Expected: []sql.Row{ {"public"}, }, }, { Query: `SELECT current_schema();`, Expected: []sql.Row{ {"public"}, }, }, { Query: "CREATE SCHEMA test_schema;", Expected: []sql.Row{}, }, { Query: `SET SEARCH_PATH TO test_schema;`, Expected: []sql.Row{}, }, { Query: `SELECT current_schema();`, Expected: []sql.Row{ {"test_schema"}, }, }, { Query: `SET SEARCH_PATH TO public, test_schema;`, Expected: []sql.Row{}, }, { Query: `SELECT current_schema();`, Expected: []sql.Row{ {"public"}, }, }, { Query: `SELECT current_schema;`, Expected: []sql.Row{ {"public"}, }, }, { Query: `SET SEARCH_PATH TO test_schema, public;`, Expected: []sql.Row{}, }, { Query: `SELECT current_schema();`, Expected: []sql.Row{ {"test_schema"}, }, }, // TODO: Implement table function for current_schema { Query: `SELECT * FROM current_schema();`, Skip: true, Expected: []sql.Row{ {"public"}, }, }, { Query: `SELECT * FROM current_schema;`, Skip: true, Expected: []sql.Row{ {"public"}, }, }, }, }, { Name: "current_schemas", Assertions: []ScriptTestAssertion{ { // TODO: Not sure why Postgres does not display "$user", which is postgres here Query: `SELECT current_schemas(true);`, ExpectedColNames: []string{"current_schemas"}, Expected: []sql.Row{ {"{pg_catalog,public}"}, }, }, { // TODO: Not sure why Postgres does not display "$user" here Query: `SELECT current_schemas(false);`, Expected: []sql.Row{ {"{public}"}, }, }, { Query: "CREATE SCHEMA test_schema;", Expected: []sql.Row{}, }, { Query: `SET SEARCH_PATH TO test_schema;`, Expected: []sql.Row{}, }, { Query: `SELECT current_schemas(true);`, Expected: []sql.Row{ {"{pg_catalog,test_schema}"}, }, }, { Query: `SELECT current_schemas(false);`, Expected: []sql.Row{ {"{test_schema}"}, }, }, { Query: `SET SEARCH_PATH TO public, test_schema;`, Expected: []sql.Row{}, }, { Query: `SELECT current_schemas(true);`, Expected: []sql.Row{ {"{pg_catalog,public,test_schema}"}, }, }, { Query: `SELECT current_schemas(false);`, Expected: []sql.Row{ {"{public,test_schema}"}, }, }, }, }, { Name: "version", Assertions: []ScriptTestAssertion{ { Query: `SELECT version();`, ExpectedColNames: []string{"version"}, Expected: []sql.Row{ {"PostgreSQL 15.5"}, }, }, }, }, { Name: "col_description", Assertions: []ScriptTestAssertion{ { Query: `SELECT col_description(100, 1);`, ExpectedColNames: []string{"col_description"}, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT col_description('not_a_table'::regclass, 1);`, ExpectedErr: `relation "not_a_table" does not exist`, }, { Query: `CREATE TABLE test_table (id INT);`, Expected: []sql.Row{}, }, { Skip: true, // TODO: Implement column comments Query: `COMMENT ON COLUMN test_table.id IS 'This is col id';`, Expected: []sql.Row{}, }, { Skip: true, // TODO: Implement column object comments Query: `SELECT col_description('test_table'::regclass, 1);`, ExpectedColNames: []string{"col_description"}, Expected: []sql.Row{ {"This is col id"}, }, }, }, }, { Name: "obj_description", Assertions: []ScriptTestAssertion{ { Query: `SELECT obj_description(1003);`, ExpectedColNames: []string{"obj_description"}, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT obj_description(100, 'pg_class');`, ExpectedColNames: []string{"obj_description"}, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT obj_description('does-not-exist'::regproc, 'pg_class');`, ExpectedErr: `function "does-not-exist" does not exist`, }, { Skip: true, // TODO: Implement database object comments Query: `SELECT obj_description('sinh'::regproc, 'pg_proc');`, ExpectedColNames: []string{"col_description"}, Expected: []sql.Row{ {"hyperbolic sine"}, }, }, }, }, { Name: "shobj_description", Assertions: []ScriptTestAssertion{ { Query: `SELECT shobj_description(100, 'pg_class');`, ExpectedColNames: []string{"shobj_description"}, Expected: []sql.Row{ {nil}, }, }, { Query: `SELECT shobj_description('does-not-exist'::regproc, 'pg_class');`, ExpectedErr: `function "does-not-exist" does not exist`, }, { Skip: true, // TODO: Implement tablespaces Query: `CREATE TABLESPACE tblspc_2 LOCATION '/';`, Expected: []sql.Row{}, }, { Skip: true, // TODO: Implement shared database object comments Query: `COMMENT ON TABLESPACE tblspc_2 IS 'Store a few of the things';`, Expected: []sql.Row{}, }, { Skip: true, // TODO: Implement shared database object comments Query: `SELECT shobj_description( (SELECT oid FROM pg_tablespace WHERE spcname = 'tblspc_2'), 'pg_tablespace');`, ExpectedColNames: []string{"shobj_description"}, Expected: []sql.Row{ {"Store a few of the things"}, }, }, }, }, { Name: "format_type", Assertions: []ScriptTestAssertion{ // Without typemod { Query: `SELECT format_type('integer'::regtype, null);`, ExpectedColNames: []string{"format_type"}, Expected: []sql.Row{ {"integer"}, }, }, { Query: `SELECT format_type('character varying'::regtype, null);`, Expected: []sql.Row{ {"character varying"}, }, }, { Query: `SELECT format_type('varchar'::regtype, null);`, Expected: []sql.Row{ {"character varying"}, }, }, { Query: `SELECT format_type('date'::regtype, null);`, Expected: []sql.Row{ {"date"}, }, }, { Query: `SELECT format_type('timestamptz'::regtype, null);`, Expected: []sql.Row{ {"timestamp with time zone"}, }, }, { Query: `SELECT format_type('bool'::regtype, null);`, Expected: []sql.Row{ {"boolean"}, }, }, { Query: `SELECT format_type(1007, null);`, Expected: []sql.Row{ {"integer[]"}, }, }, { Query: `SELECT format_type('"char"'::regtype, null);`, Expected: []sql.Row{ {`"char"`}, }, }, { Query: `SELECT format_type('"char"[]'::regtype, null);`, Expected: []sql.Row{ {"\"char\"[]"}, }, }, { Query: `SELECT format_type(1002, null);`, Expected: []sql.Row{ {"\"char\"[]"}, }, }, { Query: `SELECT format_type('real[]'::regtype, null);`, Expected: []sql.Row{ {"real[]"}, }, }, // With typemod { Query: `SELECT format_type('character varying'::regtype, 100);`, Expected: []sql.Row{ {"character varying(96)"}, }, }, { Query: `SELECT format_type('text'::regtype, 0);`, Expected: []sql.Row{ {"text(0)"}, }, }, { Query: `SELECT format_type('text'::regtype, 4);`, Expected: []sql.Row{ {"text(4)"}, }, }, { Query: `SELECT format_type('text'::regtype, -1);`, Expected: []sql.Row{ {"text"}, }, }, { Query: `SELECT format_type('name'::regtype, 0);`, Expected: []sql.Row{ {"name(0)"}, }, }, { Query: `SELECT format_type('bpchar'::regtype, -1);`, Expected: []sql.Row{ {"bpchar"}, }, }, { Query: `SELECT format_type('bpchar'::regtype, 10);`, Expected: []sql.Row{ {"character(6)"}, }, }, { Query: `SELECT format_type('bpchar'::regtype, 10);`, Expected: []sql.Row{ {"character(6)"}, }, }, { Query: `SELECT format_type('character'::regtype, 4);`, Expected: []sql.Row{ {"character"}, }, }, { Query: `SELECT format_type('varchar'::regtype, 0);`, Expected: []sql.Row{ {"character varying"}, }, }, { Query: `SELECT format_type('"char"'::regtype, 0);`, Expected: []sql.Row{ {"\"char\"(0)"}, }, }, { Query: `SELECT format_type('numeric'::regtype, 12);`, Expected: []sql.Row{ {"numeric(0,8)"}, }, }, // OID does not exist { Query: `SELECT format_type(874938247, 20);`, ExpectedColNames: []string{"format_type"}, Expected: []sql.Row{ {"???"}, }, }, { Query: `SELECT format_type(874938247, null);`, ExpectedColNames: []string{"format_type"}, Expected: []sql.Row{ {"???"}, }, }, }, }, { Name: "pg_get_constraintdef", SetUpScript: []string{ `CREATE TABLE testing (pk INT primary key, v1 INT UNIQUE);`, `CREATE TABLE testing2 (pk INT primary key, pktesting INT REFERENCES testing(pk), v1 TEXT);`, `CREATE TABLE testing3 (pk1 INT, pk2 INT, PRIMARY KEY (pk1, pk2));`, // TODO: Uncomment when check constraints supported // `ALTER TABLE testing2 ADD CONSTRAINT v1_check CHECK (v1 != '');`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT pg_get_constraintdef(845743985);`, ExpectedColNames: []string{"pg_get_constraintdef"}, Expected: []sql.Row{{""}}, }, { Query: `SELECT pg_get_constraintdef(oid) FROM pg_catalog.pg_constraint WHERE conrelid='testing'::regclass;`, ExpectedColNames: []string{"pg_get_constraintdef"}, Expected: []sql.Row{ {"PRIMARY KEY (pk)"}, {"UNIQUE (v1)"}, }, }, { Query: `SELECT pg_get_constraintdef(oid) FROM pg_catalog.pg_constraint WHERE conrelid='testing2'::regclass;`, Expected: []sql.Row{ {"FOREIGN KEY (pktesting) REFERENCES testing(pk)"}, {"PRIMARY KEY (pk)"}, }, }, { Query: `SELECT pg_get_constraintdef(oid) FROM pg_catalog.pg_constraint WHERE conrelid='testing3'::regclass;`, Expected: []sql.Row{ {"PRIMARY KEY (pk1, pk2)"}, }, }, { Query: `SELECT pg_get_constraintdef(oid, true) FROM pg_catalog.pg_constraint WHERE conrelid='testing3'::regclass;`, Expected: []sql.Row{ {"PRIMARY KEY (pk1, pk2)"}, }, }, { Query: `SELECT pg_get_constraintdef(oid, false) FROM pg_catalog.pg_constraint WHERE conrelid='testing3'::regclass;`, Expected: []sql.Row{ {"PRIMARY KEY (pk1, pk2)"}, }, }, }, }, { Name: "pg_get_ruledef", Assertions: []ScriptTestAssertion{ { Query: `SELECT pg_get_ruledef(845743985);`, ExpectedColNames: []string{"pg_get_ruledef"}, Expected: []sql.Row{{nil}}, }, { Query: `SELECT pg_get_ruledef(845743985, true);`, ExpectedColNames: []string{"pg_get_ruledef"}, Expected: []sql.Row{{nil}}, }, { Query: `SELECT pg_get_ruledef(845743985, false);`, ExpectedColNames: []string{"pg_get_ruledef"}, Expected: []sql.Row{{nil}}, }, }, }, { Name: "pg_get_expr", SetUpScript: []string{ `CREATE TABLE testing (id INT primary key);`, `CREATE TABLE temperature (celsius SMALLINT NOT NULL, fahrenheit SMALLINT NOT NULL GENERATED ALWAYS AS ((celsius * 9/5) + 32) STORED);`, }, Assertions: []ScriptTestAssertion{ { Skip: true, // TODO: pg_attrdef.adbin not implemented Query: `SELECT pg_get_expr(adbin, adrelid) FROM pg_catalog.pg_attrdef WHERE adrelid = 'temperature'::regclass;`, ExpectedColNames: []string{"pg_get_expr"}, Expected: []sql.Row{{"(celsius * 9 / 5 + 32)"}}, }, { Query: `SELECT indexrelid, pg_get_expr(indpred, indrelid) FROM pg_catalog.pg_index WHERE indrelid='testing'::regclass;`, ExpectedColNames: []string{"indexrelid", "pg_get_expr"}, Expected: []sql.Row{{3757635986, nil}}, }, { Query: `SELECT indexrelid, pg_get_expr(indpred, indrelid, true) FROM pg_catalog.pg_index WHERE indrelid='testing'::regclass;`, Expected: []sql.Row{{3757635986, nil}}, }, { Query: `SELECT indexrelid, pg_get_expr(indpred, indrelid, NULL) FROM pg_catalog.pg_index WHERE indrelid='testing'::regclass;`, Expected: []sql.Row{{3757635986, nil}}, }, }, }, { Name: "pg_get_serial_sequence", SetUpScript: []string{ `create table t0 (id INTEGER NOT NULL PRIMARY KEY);`, `create table t1 (id SERIAL PRIMARY KEY);`, `create sequence t2_id_seq START 1 INCREMENT 3;`, `create table t2 (id INTEGER NOT NULL DEFAULT nextval('t2_id_seq'));`, // TODO: ALTER SEQUENCE OWNED BY is not supported yet. When the sequence is created // explicitly, separate from the column, the owner must be updated before // pg_get_serial_sequence() will identify it. // `ALTER SEQUENCE t2_id_seq OWNED BY t2.id;`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT pg_get_serial_sequence('doesnotexist.t1', 'id');`, ExpectedErr: "does not exist", }, { Query: `SELECT pg_get_serial_sequence('doesnotexist', 'id');`, ExpectedErr: "does not exist", }, { Query: `SELECT pg_get_serial_sequence('t0', 'doesnotexist');`, ExpectedErr: "does not exist", }, { // No sequence for column returns null Query: `SELECT pg_get_serial_sequence('t0', 'id');`, ExpectedColNames: []string{"pg_get_serial_sequence"}, Expected: []sql.Row{{nil}}, }, { Query: `SELECT pg_get_serial_sequence('public.t1', 'id');`, ExpectedColNames: []string{"pg_get_serial_sequence"}, Expected: []sql.Row{{"public.t1_id_seq"}}, }, { Query: `SELECT pg_get_serial_sequence('"public"."t1"', 'id');`, ExpectedColNames: []string{"pg_get_serial_sequence"}, Expected: []sql.Row{{"public.t1_id_seq"}}, }, { // Test with no schema specified Query: `SELECT pg_get_serial_sequence('t1', 'id');`, ExpectedColNames: []string{"pg_get_serial_sequence"}, Expected: []sql.Row{{"public.t1_id_seq"}}, }, { // TODO: This test shouldn't pass until we're able to use // ALTER SEQUENCE OWNED BY to set the owning column. Skip: true, Query: `SELECT pg_get_serial_sequence('t2', 'id');`, ExpectedColNames: []string{"pg_get_serial_sequence"}, Expected: []sql.Row{{"public.t2_id_seq"}}, }, }, }, { Name: "current_setting function", Assertions: []ScriptTestAssertion{ { Query: "SET timezone TO '+00:00';", Expected: []sql.Row{}, }, { Query: "SELECT current_setting('timezone')", Expected: []sql.Row{{"+00:00"}}, }, { Query: "SELECT current_setting('wrong_input')", ExpectedErr: `unrecognized configuration parameter "wrong_input"`, }, { Query: "SELECT current_setting('wrong_input', true)", Expected: []sql.Row{{nil}}, }, { Query: "SELECT current_setting('wrong_input', false)", ExpectedErr: `unrecognized configuration parameter "wrong_input"`, }, }, }, }) } func TestJsonFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "json_build_array", Assertions: []ScriptTestAssertion{ { Query: `SELECT json_build_array(1, 2, 3);`, ExpectedColNames: []string{"json_build_array"}, Expected: []sql.Row{{`[1,2,3]`}}, }, { Query: `SELECT json_build_array(1, '2', 3);`, ExpectedColNames: []string{"json_build_array"}, Expected: []sql.Row{{`[1,"2",3]`}}, }, { Query: `SELECT json_build_array();`, Skip: true, // variadic functions can't handle 0 arguments right now ExpectedColNames: []string{"json_build_array"}, Expected: []sql.Row{{`[]`}}, }, }, }, { Name: "json_build_object", Assertions: []ScriptTestAssertion{ { Query: `SELECT json_build_object('a', 2, 'b', 4);`, ExpectedColNames: []string{"json_build_object"}, Expected: []sql.Row{{`{"a":2,"b":4}`}}, }, { Query: `SELECT json_build_object('a', 2, 'b');`, ExpectedErr: "even number", }, { Query: `SELECT json_build_object(1, 2, 'b', 3);`, ExpectedColNames: []string{"json_build_object"}, Expected: []sql.Row{{`{"1":2,"b":3}`}}, }, }, }, { Name: "jsonb_build_array", Assertions: []ScriptTestAssertion{ { Query: `SELECT jsonb_build_array(1, 2, 3);`, ExpectedColNames: []string{"jsonb_build_array"}, Expected: []sql.Row{{`[1, 2, 3]`}}, }, { Query: `SELECT jsonb_build_array(1, '2', 3);`, ExpectedColNames: []string{"jsonb_build_array"}, Expected: []sql.Row{{`[1, "2", 3]`}}, }, { Query: `SELECT jsonb_build_array();`, Skip: true, // variadic functions can't handle 0 arguments right now ExpectedColNames: []string{"jsonb_build_array"}, Expected: []sql.Row{{`[]`}}, }, }, }, { Name: "jsonb_build_object", Assertions: []ScriptTestAssertion{ { Query: `SELECT jsonb_build_object('a', 2, 'b', 4);`, ExpectedColNames: []string{"jsonb_build_object"}, Expected: []sql.Row{{`{"a": 2, "b": 4}`}}, }, { Query: `SELECT jsonb_build_object('a', 2, 'b');`, ExpectedErr: "even number", }, { Query: `SELECT jsonb_build_object(1, 2, 'b', 3);`, ExpectedColNames: []string{"jsonb_build_object"}, Expected: []sql.Row{{`{"1": 2, "b": 3}`}}, }, }, }, { Name: "row_to_json anonymous row", Assertions: []ScriptTestAssertion{ { Query: `SELECT row_to_json(row(1, 'foo'))`, Expected: []sql.Row{{`{"f1":1,"f2":"foo"}`}}, }, { Query: `SELECT row_to_json(row(1, true, null))`, Expected: []sql.Row{{`{"f1":1,"f2":true,"f3":null}`}}, }, { Query: `SELECT row_to_json(row(1, 2, 3), false)`, Expected: []sql.Row{{`{"f1":1,"f2":2,"f3":3}`}}, }, { Query: `SELECT row_to_json(row(1, 2, 3), true)`, Expected: []sql.Row{{`{"f1":1,` + "\n " + `"f2":2,` + "\n " + `"f3":3}`}}, }, }, }, { Name: "row_to_json named columns", SetUpScript: []string{ `CREATE TABLE rtj_test (id int4, name text)`, `INSERT INTO rtj_test VALUES (1, 'Alice'), (2, 'Bob')`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT row_to_json(t) FROM rtj_test t ORDER BY id`, Expected: []sql.Row{{`{"id":1,"name":"Alice"}`}, {`{"id":2,"name":"Bob"}`}}, }, { Query: `SELECT row_to_json(t.*) FROM rtj_test t ORDER BY id`, Expected: []sql.Row{{`{"id":1,"name":"Alice"}`}, {`{"id":2,"name":"Bob"}`}}, }, { Skip: true, // TODO: table alias as type Query: `SELECT row_to_json(d.*) FROM (SELECT * FROM rtj_test) d ORDER BY id`, Expected: []sql.Row{{`{"id":1,"name":"Alice"}`}, {`{"id":2,"name":"Bob"}`}}, }, }, }, }) } func TestArrayFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "unnest", SetUpScript: []string{ `CREATE TABLE testing (id INT primary key, val1 smallint[]);`, `INSERT INTO testing VALUES (1, '{}'), (2, '{1}'), (3, '{1, 2}');`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT unnest(val1) FROM testing WHERE id=1;`, ExpectedColNames: []string{"unnest"}, Expected: []sql.Row{}, }, { Query: `SELECT unnest(val1) FROM testing WHERE id=2;`, ExpectedColNames: []string{"unnest"}, Expected: []sql.Row{{1}}, }, { Query: `SELECT unnest(val1) FROM testing WHERE id=3;`, Expected: []sql.Row{{1}, {2}}, }, { Skip: true, // TODO: fix for this in gms breaks regression test Query: `select * from unnest(array[1,2,3]);`, Expected: []sql.Row{{1}, {2}, {3}}, }, }, }, { Name: "array_to_json", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT array_to_json(ARRAY[1, 2, 3])`, Expected: []sql.Row{{"[1,2,3]"}}, }, { Query: `SELECT array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);`, Expected: []sql.Row{{`[{"a":1},{"b":[2,3]}]`}}, }, { Query: `SELECT array_to_json(ARRAY[1.5, 2.5]::float8[])`, Expected: []sql.Row{{"[1.5,2.5]"}}, }, { Query: `SELECT array_to_json(ARRAY[true, false])`, Expected: []sql.Row{{"[true,false]"}}, }, { Query: `SELECT array_to_json(ARRAY['a', 'b', 'c'])`, Expected: []sql.Row{{`["a","b","c"]`}}, }, { Query: `SELECT array_to_json(ARRAY[1, NULL, 3])`, Expected: []sql.Row{{"[1,null,3]"}}, }, { Skip: true, // TODO: multidimensional array literals are not yet supported Query: `SELECT array_to_json('{{1,2},{3,4}}'::int[])`, Expected: []sql.Row{{"[[1,2],[3,4]]"}}, }, { Query: `SELECT array_to_json(ARRAY[1, 2, 3], false);`, Expected: []sql.Row{{"[1,2,3]"}}, }, { Query: `SELECT array_to_json(ARRAY[1, 2, 3], true)`, Expected: []sql.Row{{"[1,\n 2,\n 3]"}}, }, }, }, { Name: "array_to_string", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT array_to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*')`, Expected: []sql.Row{{"1,2,3,*,5"}}, }, { Query: `SELECT array_to_string(ARRAY[1, 2, 3, NULL, 5], ',')`, Expected: []sql.Row{{"1,2,3,5"}}, }, { Query: `SELECT array_to_string(ARRAY[37.89, 1.2], '_');`, Expected: []sql.Row{{"37.89_1.2"}}, }, { Skip: true, // TODO: we currently return "37_1" Query: `SELECT array_to_string(ARRAY[37.89::int4, 1.2::int4], '_');`, Expected: []sql.Row{{"38_1"}}, }, }, }, { Name: "array_upper", Assertions: []ScriptTestAssertion{ { Query: `select array_upper(ARRAY[1,2,3,4], 1);`, Expected: []sql.Row{{4}}, }, { Skip: true, // TODO: multi-dimensional is not supported yet Query: `select array_upper(ARRAY[1,2,3,4], 2);`, Expected: []sql.Row{{nil}}, }, }, }, { Name: "array_cat", Assertions: []ScriptTestAssertion{ { Query: `select array_cat(ARRAY[1,2,3], ARRAY[4,5]);`, Expected: []sql.Row{{"{1,2,3,4,5}"}}, }, { Query: `select array_cat(NULL, ARRAY[4,5]);`, Expected: []sql.Row{{"{4,5}"}}, }, { Query: `select array_cat(ARRAY[1,2,3], NULL);`, Expected: []sql.Row{{"{1,2,3}"}}, }, { Query: `select array_cat(NULL, NULL);`, Expected: []sql.Row{{nil}}, }, }, }, { Name: "array_length", Assertions: []ScriptTestAssertion{ { Query: `select array_length(ARRAY[1,2,3,4,5], 1);`, Expected: []sql.Row{{5}}, }, }, }, { Name: "array_position and array_positions", Assertions: []ScriptTestAssertion{ { Query: `SELECT array_position(ARRAY[1,2,3,4,5], 4);`, Expected: []sql.Row{{4}}, }, { Query: `SELECT array_position(ARRAY[1,4,2,3,4,5,4], 4, 3);`, Expected: []sql.Row{{5}}, }, { Query: `select array_position(NULL, 1);`, Expected: []sql.Row{{nil}}, }, { Query: `select array_position(ARRAY[1,4,2,3,4,5,4], NULL);`, Expected: []sql.Row{{nil}}, }, { Query: `select array_position(NULL, NULL);`, Expected: []sql.Row{{nil}}, }, { Query: `SELECT array_positions(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], 4);`, Expected: []sql.Row{{"{4,10}"}}, }, { Query: `select array_positions(NULL, 1);`, Expected: []sql.Row{{nil}}, }, { Query: `select array_positions(ARRAY[1,4,2,3,4,5,4], NULL);`, Expected: []sql.Row{{"{}"}}, }, { Query: `select array_positions(NULL, NULL);`, Expected: []sql.Row{{nil}}, }, }, }, { Name: "array_prepend", Assertions: []ScriptTestAssertion{ { Query: `SELECT array_prepend(NULL, NULL);`, Expected: []sql.Row{{"{NULL}"}}, }, { Query: `SELECT array_prepend(NULL, ARRAY[6]);`, Expected: []sql.Row{{"{NULL,6}"}}, }, { Query: `SELECT array_prepend(5, NULL);`, Expected: []sql.Row{{"{5}"}}, }, { Query: `SELECT array_prepend(5, ARRAY[6]);`, Expected: []sql.Row{{"{5,6}"}}, }, }, }, }) } func TestSchemaVisibilityInquiryFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Skip: true, // TODO: not supported Name: "pg_function_is_visible", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT pg_function_is_visible(1342177280);`, Expected: []sql.Row{{"t"}}, }, { Query: `SELECT pg_function_is_visible(22);`, // invalid Expected: []sql.Row{{"f"}}, }, }, }, { Name: "pg_table_is_visible", SetUpScript: []string{ "CREATE SCHEMA myschema;", "SET search_path TO myschema;", "CREATE TABLE mytable (id int, name text);", "INSERT INTO mytable VALUES (1,'desk'), (2,'chair');", "CREATE VIEW myview AS SELECT name FROM mytable;", "CREATE SCHEMA testschema;", "SET search_path TO testschema;", `CREATE TABLE test_table (pk INT primary key, v1 INT UNIQUE);`, "INSERT INTO test_table VALUES (1,5), (2,7);", "CREATE INDEX test_index ON test_table(v1);", "CREATE SEQUENCE test_seq START 39;", }, Assertions: []ScriptTestAssertion{ { Query: `SELECT c.oid, c.relname AS table_name, n.nspname AS table_schema FROM pg_catalog.pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE (n.nspname='myschema' OR n.nspname='testschema') AND left(relname, 5) <> 'dolt_' order by relname;`, Expected: []sql.Row{ {3905781870, "mytable", "myschema"}, {3983475213, "myview", "myschema"}, {3057657334, "test_index", "testschema"}, {1539973141, "test_seq", "testschema"}, {1952237395, "test_table", "testschema"}, {3508950454, "test_table_pkey", "testschema"}, {2590613415, "test_table_v1_key", "testschema"}, }, }, { Query: `SHOW search_path;`, Expected: []sql.Row{{"testschema"}}, }, { Query: `select pg_table_is_visible(3057657334);`, // index from testschema Expected: []sql.Row{{"t"}}, }, { Query: `select pg_table_is_visible(1952237395);`, // table from testschema Expected: []sql.Row{{"t"}}, }, { Query: `select pg_table_is_visible(1539973141);`, // sequence from testschema Expected: []sql.Row{{"t"}}, }, { Query: `select pg_table_is_visible(3983475213);`, // view from myschema Expected: []sql.Row{{"f"}}, }, { Query: `SET search_path = 'myschema';`, Expected: []sql.Row{}, }, { Query: `SHOW search_path;`, Expected: []sql.Row{{"myschema"}}, }, { Query: `select pg_table_is_visible(3983475213);`, // view from myschema Expected: []sql.Row{{"t"}}, }, { Query: `select pg_table_is_visible(3905781870);`, // table from myschema Expected: []sql.Row{{"t"}}, }, }, }, { Name: "pg_type_is_visible", SetUpScript: []string{ "CREATE SCHEMA myschema;", "SET search_path TO myschema;", "CREATE DOMAIN mydomain AS text;", "CREATE TYPE myenum AS ENUM ('a', 'b', 'c');", "CREATE SCHEMA testschema;", "SET search_path TO testschema;", "CREATE DOMAIN test_domain AS int;", "CREATE TYPE test_enum AS ENUM ('x', 'y', 'z');", }, Assertions: []ScriptTestAssertion{ { Query: `SELECT t.oid, t.typname, n.nspname FROM pg_catalog.pg_type t JOIN pg_namespace n ON n.oid = t.typnamespace WHERE n.nspname='myschema' OR n.nspname='testschema' ORDER BY t.typname;`, Expected: []sql.Row{ {4183151504, "_mydomain", "myschema"}, {2179370508, "_myenum", "myschema"}, {2560281972, "_test_domain", "testschema"}, {2798112218, "_test_enum", "testschema"}, {340132571, "mydomain", "myschema"}, {1684884017, "myenum", "myschema"}, {2272253470, "test_domain", "testschema"}, {1117094145, "test_enum", "testschema"}, }, }, { Query: `SHOW search_path;`, Expected: []sql.Row{{"testschema"}}, }, { Query: `SELECT pg_type_is_visible(2272253470);`, // test_domain from testschema Expected: []sql.Row{{"t"}}, }, { Query: `SELECT pg_type_is_visible(1117094145);`, // test_enum from testschema Expected: []sql.Row{{"t"}}, }, { Query: `SELECT pg_type_is_visible(340132571);`, // mydomain from myschema Expected: []sql.Row{{"f"}}, }, { Query: `SELECT pg_type_is_visible(1684884017);`, // myenum from myschema Expected: []sql.Row{{"f"}}, }, { Query: `SET search_path = 'myschema';`, Expected: []sql.Row{}, }, { Query: `SHOW search_path;`, Expected: []sql.Row{{"myschema"}}, }, { Query: `SELECT pg_type_is_visible(340132571);`, // mydomain from myschema Expected: []sql.Row{{"t"}}, }, { Query: `SELECT pg_type_is_visible(1684884017);`, // myenum from myschema Expected: []sql.Row{{"t"}}, }, { Query: `SELECT pg_type_is_visible(2272253470);`, // test_domain from testschema Expected: []sql.Row{{"f"}}, }, { Query: `SELECT pg_type_is_visible(1117094145);`, // test_enum from testschema Expected: []sql.Row{{"f"}}, }, { Query: `SELECT pg_type_is_visible(999999);`, // non-existent type oid Expected: []sql.Row{{"f"}}, }, { Query: `SET search_path = 'pg_catalog';`, Expected: []sql.Row{}, }, { Query: `SELECT pg_type_is_visible('text'::regtype::oid);`, // built-in type should be visible in pg_catalog Expected: []sql.Row{{"t"}}, }, { Query: `SELECT pg_type_is_visible('int4'::regtype::oid);`, // built-in type should be visible in pg_catalog Expected: []sql.Row{{"t"}}, }, }, }, }) } func TestSystemCatalogInformationFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "pg_encoding_to_char", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = 'postgres';`, Expected: []sql.Row{ {"UTF8"}, }, }, }, }, { Name: "pg_get_functiondef", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { // TODO: not supported yet Query: `SELECT pg_get_functiondef(22)`, Expected: []sql.Row{ {""}, }, }, }, }, { Name: "pg_get_function_result", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { // TODO: not supported yet Query: `SELECT pg_get_function_result(22)`, Expected: []sql.Row{ {""}, }, }, }, }, { Name: "pg_get_triggerdef", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { // TODO: triggers are not supported yet Query: `SELECT pg_get_triggerdef(22)`, Expected: []sql.Row{ {""}, }, }, }, }, { Name: "pg_get_userbyid", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { // TODO: users and roles are not supported yet Query: `SELECT pg_get_userbyid(22)`, Expected: []sql.Row{ {"postgres"}, }, }, }, }, { Name: "pg_get_viewdef", SetUpScript: []string{ "CREATE TABLE test (id int, name text)", "INSERT INTO test VALUES (1,'desk'), (2,'chair')", "CREATE VIEW test_view AS SELECT name FROM test", }, Assertions: []ScriptTestAssertion{ { Query: `SELECT c.oid, c.relname AS table_name, n.nspname AS table_schema FROM pg_catalog.pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE (n.nspname='myschema' OR n.nspname='public') and left(relname, 5) <> 'dolt_';`, Expected: []sql.Row{ {2707638987, "test_view", "public"}, {1397286223, "test", "public"}, }, }, { Query: `select pg_get_viewdef(2707638987);`, Expected: []sql.Row{{"SELECT name FROM test"}}, }, }, }, }) } func TestDateAndTimeFunction(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "extract from date", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT EXTRACT(CENTURY FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("21")}}, }, { Query: `SELECT EXTRACT(CENTURY FROM DATE '0002-12-31 BC');`, Expected: []sql.Row{{Numeric("-1")}}, }, { Query: `SELECT EXTRACT(DAY FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("2")}}, }, { Query: `SELECT EXTRACT(DECADE FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("202")}}, }, { Query: `SELECT EXTRACT(DOW FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("3")}}, }, { Query: `SELECT EXTRACT(DOY FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("33")}}, }, { Query: `SELECT EXTRACT(EPOCH FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("1643760000")}}, }, { Query: `SELECT EXTRACT(HOUR FROM DATE '2022-02-02');`, ExpectedErr: `unit "hour" not supported for type date`, }, { Query: `SELECT EXTRACT(ISODOW FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("3")}}, }, { Query: `SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-01');`, Expected: []sql.Row{{Numeric("2005")}}, }, { Query: `SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-02');`, Expected: []sql.Row{{Numeric("2006")}}, }, { Query: `SELECT extract(julian from date '2021-06-23');`, Expected: []sql.Row{{Numeric("2459389")}}, }, { Query: `SELECT EXTRACT(MICROSECONDS FROM DATE '2022-02-02');`, ExpectedErr: `unit "microseconds" not supported for type date`, }, { Query: `SELECT EXTRACT(MILLENNIUM FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("3")}}, }, { Query: `SELECT EXTRACT(MILLISECONDS FROM DATE '2022-02-02');`, ExpectedErr: `unit "milliseconds" not supported for type date`, }, { Query: `SELECT EXTRACT(MINUTE FROM DATE '2022-02-02');`, ExpectedErr: `unit "minute" not supported for type date`, }, { Query: `SELECT EXTRACT(MONTH FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("2")}}, }, { Query: `SELECT EXTRACT(QUARTER FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("1")}}, }, { Query: `SELECT EXTRACT(SECOND FROM DATE '2022-02-02');`, ExpectedErr: `unit "second" not supported for type date`, }, { Query: `SELECT EXTRACT(TIMEZONE FROM DATE '2022-02-02');`, ExpectedErr: `unit "timezone" not supported for type date`, }, { Query: `SELECT EXTRACT(TIMEZONE_HOUR FROM DATE '2022-02-02');`, ExpectedErr: `unit "timezone_hour" not supported for type date`, }, { Query: `SELECT EXTRACT(TIMEZONE_MINUTE FROM DATE '2022-02-02');`, ExpectedErr: `unit "timezone_minute" not supported for type date`, }, { Query: `SELECT EXTRACT(WEEK FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("5")}}, }, { Query: `SELECT EXTRACT(YEAR FROM DATE '2022-02-02');`, Expected: []sql.Row{{Numeric("2022")}}, }, }, }, { Name: "extract from time without time zone", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT EXTRACT(CENTURY FROM TIME '17:12:28.5');`, ExpectedErr: `unit "century" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(DAY FROM TIME '17:12:28.5');`, ExpectedErr: `unit "day" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(DECADE FROM TIME '17:12:28.5');`, ExpectedErr: `unit "decade" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(DOW FROM TIME '17:12:28.5');`, ExpectedErr: `unit "dow" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(DOY FROM TIME '17:12:28.5');`, ExpectedErr: `unit "doy" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(EPOCH FROM TIME '17:12:28.5');`, Expected: []sql.Row{{Numeric("61948.500000")}}, }, { Query: `SELECT EXTRACT(HOUR FROM TIME '17:12:28.5');`, Expected: []sql.Row{{Numeric("17")}}, }, { Query: `SELECT EXTRACT(ISODOW FROM TIME '17:12:28.5');`, ExpectedErr: `unit "isodow" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(ISOYEAR FROM TIME '17:12:28.5');`, ExpectedErr: `unit "isoyear" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(JULIAN FROM TIME '17:12:28.5');`, ExpectedErr: `unit "julian" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(MICROSECONDS FROM TIME '17:12:28.5');`, Expected: []sql.Row{{Numeric("28500000")}}, }, { Query: `SELECT EXTRACT(MILLENNIUM FROM TIME '17:12:28.5');`, ExpectedErr: `unit "millennium" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(MILLISECONDS FROM TIME '17:12:28.5');`, Expected: []sql.Row{{Numeric("28500.000")}}, }, { Query: `SELECT EXTRACT(MINUTE FROM TIME '17:12:28.5');`, Expected: []sql.Row{{Numeric("12")}}, }, { Query: `SELECT EXTRACT(MONTH FROM TIME '17:12:28.5');`, ExpectedErr: `unit "month" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(QUARTER FROM TIME '17:12:28.5');`, ExpectedErr: `unit "quarter" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(SECOND FROM TIME '17:12:28.5');`, Expected: []sql.Row{{Numeric("28.500000")}}, }, { Query: `SELECT EXTRACT(TIMEZONE FROM TIME '17:12:28.5');`, ExpectedErr: `unit "timezone" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(TIMEZONE_HOUR FROM TIME '17:12:28.5');`, ExpectedErr: `unit "timezone_hour" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(TIMEZONE_MINUTE FROM TIME '17:12:28.5');`, ExpectedErr: `unit "timezone_minute" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(WEEK FROM TIME '17:12:28.5');`, ExpectedErr: `unit "week" not supported for type time without time zone`, }, { Query: `SELECT EXTRACT(YEAR FROM TIME '17:12:28.5');`, ExpectedErr: `unit "year" not supported for type time without time zone`, }, }, }, { Name: "extract from time with time zone", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT EXTRACT(CENTURY FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "century" not supported for type time`, }, { Query: `SELECT EXTRACT(DAY FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "day" not supported for type time`, }, { Query: `SELECT EXTRACT(DECADE FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "decade" not supported for type time`, }, { Query: `SELECT EXTRACT(DOW FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "dow" not supported for type time`, }, { Query: `SELECT EXTRACT(DOY FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "doy" not supported for type time`, }, { Query: `SELECT EXTRACT(EPOCH FROM TIME WITH TIME ZONE '17:12:28.5-03');`, Expected: []sql.Row{{Numeric("72748.500000")}}, }, { Query: `SELECT EXTRACT(HOUR FROM TIME WITH TIME ZONE '17:12:28.5-03');`, Expected: []sql.Row{{Numeric("17")}}, }, { Query: `SELECT EXTRACT(ISODOW FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "isodow" not supported for type time`, }, { Query: `SELECT EXTRACT(ISOYEAR FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "isoyear" not supported for type time`, }, { Query: `SELECT EXTRACT(JULIAN FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "julian" not supported for type time`, }, { Query: `SELECT EXTRACT(MICROSECONDS FROM TIME WITH TIME ZONE '17:12:28.5-03');`, Expected: []sql.Row{{Numeric("28500000")}}, }, { Query: `SELECT EXTRACT(MILLENNIUM FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "millennium" not supported for type time`, }, { Query: `SELECT EXTRACT(MILLISECONDS FROM TIME WITH TIME ZONE '17:12:28.5-03');`, Expected: []sql.Row{{Numeric("28500.000")}}, }, { Query: `SELECT EXTRACT(MINUTE FROM TIME WITH TIME ZONE '17:12:28.5-03');`, Expected: []sql.Row{{Numeric("12")}}, }, { Query: `SELECT EXTRACT(MONTH FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "month" not supported for type time`, }, { Query: `SELECT EXTRACT(QUARTER FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "quarter" not supported for type time`, }, { Query: `SELECT EXTRACT(SECOND FROM TIME WITH TIME ZONE '17:12:28.5-03');`, Expected: []sql.Row{{Numeric("28.500000")}}, }, { Query: `SELECT EXTRACT(TIMEZONE FROM TIME WITH TIME ZONE '17:12:28.5+03');`, Expected: []sql.Row{{Numeric("10800")}}, }, { Query: `SELECT EXTRACT(TIMEZONE_HOUR FROM TIME WITH TIME ZONE '17:12:28.5-03');`, Expected: []sql.Row{{Numeric("-3")}}, }, { Query: `SELECT EXTRACT(TIMEZONE_MINUTE FROM TIME WITH TIME ZONE '17:12:28.5-03:45');`, Expected: []sql.Row{{Numeric("-45")}}, }, { Query: `SELECT EXTRACT(WEEK FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "week" not supported for type time`, }, { Query: `SELECT EXTRACT(YEAR FROM TIME WITH TIME ZONE '17:12:28.5-03');`, ExpectedErr: `unit "year" not supported for type time`, }, }, }, { Name: "extract from timestamp without time zone", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT EXTRACT(CENTURY FROM TIMESTAMP '2000-12-16 12:21:13');`, Expected: []sql.Row{{Numeric("20")}}, }, { Query: `SELECT EXTRACT(CENTURY FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("21")}}, }, { Query: `SELECT EXTRACT(DAY FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("16")}}, }, { Query: `SELECT EXTRACT(DECADE FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("200")}}, }, { Query: `SELECT EXTRACT(DOW FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("5")}}, }, { Query: `SELECT EXTRACT(DOY FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("47")}}, }, { Query: `SELECT EXTRACT(EPOCH FROM TIMESTAMP '2001-02-16 20:38:40.12');`, Expected: []sql.Row{{Numeric("982355920.120000")}}, }, { Query: `SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("20")}}, }, { Query: `SELECT EXTRACT(ISODOW FROM TIMESTAMP '2001-02-18 20:38:40');`, Expected: []sql.Row{{Numeric("7")}}, }, { Query: `SELECT EXTRACT(ISOYEAR FROM TIMESTAMP '2001-02-18 20:38:40');`, Expected: []sql.Row{{Numeric("2001")}}, }, { Skip: true, // TODO: not supported yet Query: `SELECT EXTRACT(JULIAN FROM TIMESTAMP '2001-02-18 20:38:40');`, Expected: []sql.Row{{Numeric("2451959.86018518518518518519")}}, }, { Query: `SELECT EXTRACT(MICROSECONDS FROM TIMESTAMP '2001-02-18 20:38:40');`, Expected: []sql.Row{{Numeric("40000000")}}, }, { Query: `SELECT EXTRACT(MILLENNIUM FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("3")}}, }, { Query: `SELECT EXTRACT(MILLENNIUM FROM TIMESTAMP '2000-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("2")}}, }, { Query: `SELECT EXTRACT(MILLISECONDS FROM TIMESTAMP '2000-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("40000.000")}}, }, { Query: `SELECT EXTRACT(MINUTE FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("38")}}, }, { Query: `SELECT EXTRACT(MONTH FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("2")}}, }, { Query: `SELECT EXTRACT(QUARTER FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("1")}}, }, { Query: `SELECT EXTRACT(SECOND FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("40.000000")}}, }, { Query: `SELECT EXTRACT(TIMEZONE FROM TIMESTAMP '2001-02-16 20:38:40');`, ExpectedErr: `unit "timezone" not supported for type timestamp without time zone`, }, { Query: `SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP '2001-02-16 20:38:40');`, ExpectedErr: `unit "timezone_hour" not supported for type timestamp without time zone`, }, { Query: `SELECT EXTRACT(TIMEZONE_MINUTE FROM TIMESTAMP '2001-02-16 20:38:40');`, ExpectedErr: `unit "timezone_minute" not supported for type timestamp without time zone`, }, { Query: `SELECT EXTRACT(WEEK FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("7")}}, }, { Query: `SELECT EXTRACT(YEAR FROM TIMESTAMP '2001-02-16 20:38:40');`, Expected: []sql.Row{{Numeric("2001")}}, }, }, }, { // The TIMESTAMPTZ value gets converted to Local timezone / server timezone, // so set the server timezone to UTC. GitHub CI runs on UTC time zone. Name: "extract from timestamp with time zone", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SET TIMEZONE TO 'UTC';`, Expected: []sql.Row{}, }, { Query: `SELECT EXTRACT(CENTURY FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("21")}}, }, { Query: `SELECT EXTRACT(DAY FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("16")}}, }, { Query: `SELECT EXTRACT(DECADE FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("200")}}, }, { Query: `SELECT EXTRACT(DOW FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("5")}}, }, { Query: `SELECT EXTRACT(DOY FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("47")}}, }, { Query: `SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("982345120.120000")}}, }, { Query: `SELECT EXTRACT(HOUR FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("17")}}, }, { Query: `SELECT EXTRACT(ISODOW FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("5")}}, }, { Query: `SELECT EXTRACT(ISOYEAR FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("2001")}}, }, { Skip: true, // TODO: not supported yet Query: `SELECT EXTRACT(JULIAN FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("2451957.73518657407407407407")}}, }, { Skip: true, // TODO: not supported yet Query: `SELECT extract(julian from '2021-06-23 7:00:00-04'::timestamptz at time zone 'UTC+12');`, Expected: []sql.Row{{Numeric("2459388.95833333333333333333")}}, }, { Skip: true, // TODO: not supported yet Query: `SELECT extract(julian from '2021-06-23 8:00:00-04'::timestamptz at time zone 'UTC+12');`, Expected: []sql.Row{{Numeric("2459389.0000000000000000000000000000")}}, }, { Query: `SELECT EXTRACT(MICROSECONDS FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("40120000")}}, }, { Query: `SELECT EXTRACT(MILLENNIUM FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("3")}}, }, { Query: `SELECT EXTRACT(MILLISECONDS FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("40120.000")}}, }, { Query: `SELECT EXTRACT(MINUTE FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("38")}}, }, { Query: `SELECT EXTRACT(MONTH FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("2")}}, }, { Query: `SELECT EXTRACT(QUARTER FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("1")}}, }, { Query: `SELECT EXTRACT(SECOND FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("40.120000")}}, }, { Query: `SELECT EXTRACT(TIMEZONE FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("-28800")}}, }, { Query: `SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("-8")}}, }, { Query: `SELECT EXTRACT(TIMEZONE_MINUTE FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05:45');`, Expected: []sql.Row{{Numeric("0")}}, }, { Query: `SELECT EXTRACT(WEEK FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("7")}}, }, { Query: `SELECT EXTRACT(YEAR FROM TIMESTAMP WITH TIME ZONE '2001-02-16 12:38:40.12-05');`, Expected: []sql.Row{{Numeric("2001")}}, }, { Query: `SET TIMEZONE TO DEFAULT;`, Expected: []sql.Row{}, }, }, }, { Name: "extract from interval", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT EXTRACT(CENTURY FROM INTERVAL '2001 years');`, Expected: []sql.Row{{Numeric("20")}}, }, { Query: `SELECT EXTRACT(DAY FROM INTERVAL '40 days 1 minute');`, Expected: []sql.Row{{Numeric("40")}}, }, { Query: `select extract(decades from interval '1000 months');`, Expected: []sql.Row{{Numeric("8")}}, }, { Query: `SELECT EXTRACT(EPOCH FROM INTERVAL '5 days 3 hours');`, Expected: []sql.Row{{Numeric("442800.000000")}}, }, { Query: `select extract(epoch from interval '10 months 10 seconds');`, Expected: []sql.Row{{Numeric("25920010.000000")}}, }, { Query: `select extract(hours from interval '10 months 65 minutes 10 seconds');`, Expected: []sql.Row{{Numeric("1")}}, }, { Query: `select extract(microsecond from interval '10 months 65 minutes 10 seconds');`, Expected: []sql.Row{{Numeric("10000000")}}, }, { Query: `SELECT EXTRACT(MILLENNIUM FROM INTERVAL '2001 years');`, Expected: []sql.Row{{Numeric("2")}}, }, { Query: `select extract(millenniums from interval '3000 years 65 minutes 10 seconds');`, Expected: []sql.Row{{Numeric("3")}}, }, { Query: `select extract(millisecond from interval '10 months 65 minutes 10 seconds');`, Expected: []sql.Row{{Numeric("10000.000")}}, }, { Query: `select extract(minutes from interval '10 months 65 minutes 10 seconds');`, Expected: []sql.Row{{Numeric("5")}}, }, { Query: `SELECT EXTRACT(MONTH FROM INTERVAL '2 years 3 months');`, Expected: []sql.Row{{Numeric("3")}}, }, { Query: `SELECT EXTRACT(MONTH FROM INTERVAL '2 years 13 months');`, Expected: []sql.Row{{Numeric("1")}}, }, { Query: `select extract(months from interval '20 months 65 minutes 10 seconds');`, Expected: []sql.Row{{Numeric("8")}}, }, { Query: `select extract(quarter from interval '20 months 65 minutes 10 seconds');`, Expected: []sql.Row{{Numeric("3")}}, }, { Query: `select extract(seconds from interval '65 minutes 10 seconds 5 millisecond');`, Expected: []sql.Row{{Numeric("10.005000")}}, }, { Query: `select extract(years from interval '20 months 65 minutes 10 seconds');`, Expected: []sql.Row{{Numeric("1")}}, }, }, }, { Name: "age", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT age(timestamp '2001-04-10', timestamp '1957-06-13');`, Expected: []sql.Row{{"43 years 9 mons 27 days"}}, }, { Query: `SELECT age(timestamp '1957-06-13', timestamp '2001-04-10');`, Expected: []sql.Row{{"-43 years -9 mons -27 days"}}, }, { Query: `SELECT age(timestamp '2001-06-13', timestamp '2001-04-10');`, Expected: []sql.Row{{"2 mons 3 days"}}, }, { Query: `SELECT age(timestamp '2001-04-10', timestamp '2001-06-13');`, Expected: []sql.Row{{"-2 mons -3 days"}}, }, { Query: `SELECT age(timestamp '2001-04-10 12:23:33', timestamp '1957-06-13 13:23:34.4');`, Expected: []sql.Row{{"43 years 9 mons 26 days 22:59:58.6"}}, }, { Query: `SELECT age(timestamp '1957-06-13 13:23:34.4', timestamp '2001-04-10 12:23:33');`, Expected: []sql.Row{{"-43 years -9 mons -26 days -22:59:58.6"}}, }, { Query: `SELECT age(current_date);`, Expected: []sql.Row{{"00:00:00"}}, }, { Query: `SELECT age(current_date::timestamp);`, Expected: []sql.Row{{"00:00:00"}}, }, }, }, { Name: "timezone", SetUpScript: []string{`SET timezone = '+06:30'`}, Assertions: []ScriptTestAssertion{ { Query: `select timezone(interval '2 minutes', timestamp with time zone '2001-02-16 20:38:40.12-05');`, Expected: []sql.Row{{"2001-02-17 01:40:40.12"}}, }, { Query: `select timezone('UTC', timestamp with time zone '2001-02-16 20:38:40.12-05');`, Expected: []sql.Row{{"2001-02-17 01:38:40.12"}}, }, { Query: `select timezone('-04:45', time with time zone '20:38:40.12-05');`, Expected: []sql.Row{{"06:23:40.12+04:45"}}, }, { Query: `select timezone(interval '2 hours 2 minutes', time with time zone '20:38:40.12-05');`, Expected: []sql.Row{{"03:40:40.12+02:02"}}, }, { Query: `select timezone('-04:45', timestamp '2001-02-16 20:38:40.12');`, Expected: []sql.Row{{"2001-02-16 09:23:40.12-06:30"}}, }, { Query: `select timezone('-04:45:44', timestamp '2001-02-16 20:38:40.12');`, Expected: []sql.Row{{"2001-02-16 09:22:56.12-06:30"}}, }, { Query: `select '2001-02-16 20:38:40.12'::timestamp at time zone '-04:45:44';`, Expected: []sql.Row{{"2001-02-16 09:22:56.12-06:30"}}, }, { Query: `select timezone(interval '2 hours 2 minutes', timestamp '2001-02-16 20:38:40.12');`, Expected: []sql.Row{{"2001-02-16 12:06:40.12-06:30"}}, }, { Query: `select '2024-08-22 14:47:57 -07' at time zone 'utc';`, Expected: []sql.Row{{"2024-08-22 21:47:57"}}, }, { Query: `select round(extract(epoch from '2024-08-22 13:47:57-07' at time zone 'UTC')) as startup_time;`, Expected: []sql.Row{{Numeric("1724359677")}}, }, { Query: `select timestamptz '2024-08-22 13:47:57-07' at time zone 'utc';`, Expected: []sql.Row{{"2024-08-22 20:47:57"}}, }, { Query: `select timestamp '2024-08-22 13:47:57-07';`, Expected: []sql.Row{{"2024-08-22 13:47:57"}}, }, { Query: `select timestamp '2024-08-22 13:47:57-07' at time zone 'utc';`, Expected: []sql.Row{{"2024-08-22 07:17:57-06:30"}}, }, { Query: `select '2011-03-27 02:00:00'::timestamp at time zone '+01:00';`, Expected: []sql.Row{{"2011-03-26 20:30:00-06:30"}}, }, { Query: `select '2011-03-27 02:00:00'::timestamp at time zone 'UTC';`, Expected: []sql.Row{{"2011-03-26 19:30:00-06:30"}}, }, { Query: `select timezone('MSK', timestamp '2011-03-27 02:00:00');`, Expected: []sql.Row{{"2011-03-26 15:30:00-06:30"}}, }, { Query: `select '2011-03-27 02:00:00'::timestamp at time zone 'MSK';`, Expected: []sql.Row{{"2011-03-26 15:30:00-06:30"}}, }, }, }, { Name: "date_part", Assertions: []ScriptTestAssertion{ { Query: `select date_part('month', date '2001-02-16');`, Expected: []sql.Row{{float64(2)}}, }, { Query: `select date_part('minute', time without time zone '20:38:40');`, Expected: []sql.Row{{float64(38)}}, }, { Query: `select date_part('second', time with time zone '20:38:40 UTC');`, Expected: []sql.Row{{float64(40)}}, }, { Query: `select date_part('year', timestamp without time zone '2001-02-16 20:38:40');`, Expected: []sql.Row{{float64(2001)}}, }, { Query: `select date_part('day', timestamp with time zone '2001-02-16 20:38:40 UTC');`, Expected: []sql.Row{{float64(16)}}, }, { Query: `select date_part('month', interval '2 years 3 months');`, Expected: []sql.Row{{float64(3)}}, }, }, }, { Name: "date_trunc", Assertions: []ScriptTestAssertion{ { Query: `select date_trunc('hour', timestamp '2001-02-16 20:38:40');`, Expected: []sql.Row{{"2001-02-16 20:00:00"}}, }, { Query: `SET timezone to '+06:30';`, }, { Query: `select date_trunc('day', timestamp with time zone '2001-02-16 20:38:40 UTC');`, Expected: []sql.Row{{"2001-02-16 00:00:00-06:30"}}, }, { Query: `select date_trunc('day', timestamp with time zone '2001-02-16 20:38:40 UTC', '-07:00');`, Expected: []sql.Row{{"2001-02-16 10:30:00-06:30"}}, }, { Query: `SET timezone to '+06:30';`, }, { Query: `select date_trunc('hour', interval '2 days 10 hours 30 minutes');`, Expected: []sql.Row{{"2 days 10:00:00"}}, }, }, }, { Name: "to_date", Assertions: []ScriptTestAssertion{ { Query: `SELECT to_date('1 4 1902', 'Q MM YYYY');`, Expected: []sql.Row{{"1902-04-01"}}, }, { Query: `SELECT to_date('3 4 21 01', 'W MM CC YY');`, Expected: []sql.Row{{"2001-04-15"}}, }, { Query: `SELECT to_date('2458872', 'J');`, Expected: []sql.Row{{"2020-01-23"}}, }, { Query: `SELECT to_date('44-02-01 BC','YYYY-MM-DD BC');`, Expected: []sql.Row{{"0044-02-01 BC"}}, }, { Query: `SELECT to_date('-44-02-01','YYYY-MM-DD');`, Expected: []sql.Row{{"0044-02-01 BC"}}, }, { Query: `SELECT to_date('2011x 12x 18', 'YYYYxMMxDD');`, Expected: []sql.Row{{"2011-12-18"}}, }, { Query: `SELECT to_date('2015 365', 'YYYY DDD');`, Expected: []sql.Row{{"2015-12-31"}}, }, }, }, { Name: "to_timestamp", Assertions: []ScriptTestAssertion{ { Query: `SET timezone to '+06:30';`, }, { Query: `SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2011-12-18 23:38:15-06:30"}}, }, { Query: `SELECT to_timestamp('2000-01-01 12:30:45', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2000-01-01 12:30:45-06:30"}}, }, { Query: `SELECT to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS');`, Expected: []sql.Row{{"0097-02-16 08:14:30-06:30"}}, }, { Query: `SELECT to_timestamp('97/2/16 8:14:30', 'FMYYYY/FMMM/FMDD FMHH:FMMI:FMSS');`, Expected: []sql.Row{{"0097-02-16 08:14:30-06:30"}}, }, { Query: `SELECT to_timestamp('2011$03!18 23_38_15', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2011-03-18 23:38:15-06:30"}}, }, { Query: `SELECT to_timestamp('1985 January 12', 'YYYY FMMonth DD');`, Expected: []sql.Row{{"1985-01-12 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('1985 FMMonth 12', 'YYYY "FMMonth" DD');`, Expected: []sql.Row{{"1985-01-12 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('1985 \\ 12', 'YYYY \\\\ DD');`, Expected: []sql.Row{{"1985-01-12 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('My birthday-> Year: 1976, Month: May, Day: 16', '"My birthday-> Year:" YYYY, "Month:" FMMonth, "Day:" DD');`, Expected: []sql.Row{{"1976-05-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('1,582nd VIII 21', 'Y,YYYth FMRM DD');`, Expected: []sql.Row{{"1582-08-21 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('15 "text between quote marks" 98 54 45', E'HH24 "\\"text between quote marks\\"" YY MI SS');`, Expected: []sql.Row{{"1998-01-01 15:54:45-06:30"}}, }, { Query: `SELECT to_timestamp('05121445482000', 'MMDDHH24MISSYYYY');`, Expected: []sql.Row{{"2000-05-12 14:45:48-06:30"}}, }, { Query: `SELECT to_timestamp('2000January09Sunday', 'YYYYFMMonthDDFMDay');`, Expected: []sql.Row{{"2000-01-09 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('97/Feb/16', 'YYMonDD');`, ExpectedErr: "invalid value", }, { Query: `SELECT to_timestamp('97/Feb/16', 'YY:Mon:DD');`, Expected: []sql.Row{{"1997-02-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('97/Feb/16', 'FXYY:Mon:DD');`, Expected: []sql.Row{{"1997-02-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('97/Feb/16', 'FXYY/Mon/DD');`, Expected: []sql.Row{{"1997-02-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('19971116', 'YYYYMMDD');`, Expected: []sql.Row{{"1997-11-16 00:00:00-06:30"}}, }, { // TODO: this test passes but time library parsing does not allow year length to be more than 4 // the using pgx library for tests relies on it. // https://github.com/jackc/pgx/blob/master/pgtype/timestamptz.go#L312 Skip: true, Query: `SELECT to_timestamp('20000-1116', 'FXYYYY-MMDD');`, Expected: []sql.Row{{"20000-11-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('1997 AD 11 16', 'YYYY BC MM DD');`, Expected: []sql.Row{{"1997-11-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('1997 BC 11 16', 'YYYY BC MM DD');`, Expected: []sql.Row{{"1997-11-16 00:00:00-06:30 BC"}}, }, { Query: `SELECT to_timestamp('1997 A.D. 11 16', 'YYYY B.C. MM DD');`, Expected: []sql.Row{{"1997-11-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('1997 B.C. 11 16', 'YYYY B.C. MM DD');`, Expected: []sql.Row{{"1997-11-16 00:00:00-06:30 BC"}}, }, { Query: `SELECT to_timestamp('9-1116', 'Y-MMDD');`, Expected: []sql.Row{{"2009-11-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('95-1116', 'YY-MMDD');`, Expected: []sql.Row{{"1995-11-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('995-1116', 'YYY-MMDD');`, Expected: []sql.Row{{"1995-11-16 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2005426', 'YYYYWWD');`, Expected: []sql.Row{{"2005-10-15 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2005300', 'YYYYDDD');`, Expected: []sql.Row{{"2005-10-27 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2005527', 'IYYYIWID');`, Expected: []sql.Row{{"2006-01-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('005527', 'IYYIWID');`, Expected: []sql.Row{{"2006-01-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('05527', 'IYIWID');`, Expected: []sql.Row{{"2006-01-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('5527', 'IIWID');`, Expected: []sql.Row{{"2006-01-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2005364', 'IYYYIDDD');`, Expected: []sql.Row{{"2006-01-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('20050302', 'YYYYMMDD');`, Expected: []sql.Row{{"2005-03-02 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2005 03 02', 'YYYYMMDD');`, Expected: []sql.Row{{"2005-03-02 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp(' 2005 03 02', 'YYYYMMDD');`, Expected: []sql.Row{{"2005-03-02 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp(' 20050302', 'YYYYMMDD');`, Expected: []sql.Row{{"2005-03-02 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 AM', 'YYYY-MM-DD HH12:MI PM');`, Expected: []sql.Row{{"2011-12-18 11:38:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 PM', 'YYYY-MM-DD HH12:MI PM');`, Expected: []sql.Row{{"2011-12-18 23:38:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 A.M.', 'YYYY-MM-DD HH12:MI P.M.');`, Expected: []sql.Row{{"2011-12-18 11:38:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 P.M.', 'YYYY-MM-DD HH12:MI P.M.');`, Expected: []sql.Row{{"2011-12-18 23:38:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 +05', 'YYYY-MM-DD HH12:MI TZH');`, Expected: []sql.Row{{"2011-12-18 00:08:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI TZH');`, Expected: []sql.Row{{"2011-12-18 10:08:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 +05:20', 'YYYY-MM-DD HH12:MI TZH:TZM');`, Expected: []sql.Row{{"2011-12-17 23:48:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 -05:20', 'YYYY-MM-DD HH12:MI TZH:TZM');`, Expected: []sql.Row{{"2011-12-18 10:28:00-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 11:38 20', 'YYYY-MM-DD HH12:MI TZM');`, Expected: []sql.Row{{"2011-12-18 04:48:00-06:30"}}, }, { Skip: true, // TODO: support formatting TZ Query: `SELECT to_timestamp('2011-12-18 11:38 PST', 'YYYY-MM-DD HH12:MI TZ');`, ExpectedErr: `formatting field "TZ" is only supported in to_char`, }, { Query: `SELECT to_timestamp('2018-11-02 12:34:56.025', 'YYYY-MM-DD HH24:MI:SS.MS');`, Expected: []sql.Row{{"2018-11-02 12:34:56.025-06:30"}}, }, { Query: `SELECT to_timestamp('44-02-01 11:12:13 BC','YYYY-MM-DD HH24:MI:SS BC');`, Expected: []sql.Row{{"0044-02-01 11:12:13-06:30 BC"}}, }, { Query: `SELECT to_timestamp('-44-02-01 11:12:13','YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"0044-02-01 11:12:13-06:30 BC"}}, }, { Query: `SELECT to_timestamp('-44-02-01 11:12:13 BC','YYYY-MM-DD HH24:MI:SS BC');`, Expected: []sql.Row{{"0044-02-01 11:12:13-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2011-12-18 23:38:15-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2011-12-18 23:38:15-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2011-12-18 23:38:15-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2011-12-18 23:38:15-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2011-12-18 23:38:15-06:30"}}, }, { Query: `SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2011-12-18 23:38:15-06:30"}}, }, { Query: `SELECT to_timestamp('2000+ JUN', 'YYYY/MON');`, Expected: []sql.Row{{"2000-06-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp(' 2000 +JUN', 'YYYY/MON');`, Expected: []sql.Row{{"2000-06-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp(' 2000 +JUN', 'YYYY//MON');`, Expected: []sql.Row{{"2000-06-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2000 +JUN', 'YYYY//MON');`, Expected: []sql.Row{{"2000-06-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2000 + JUN', 'YYYY MON');`, Expected: []sql.Row{{"2000-06-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2000 ++ JUN', 'YYYY MON');`, Expected: []sql.Row{{"2000-06-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2000 + - JUN', 'YYYY MON');`, ExpectedErr: `invalid value "-" for "MON"`, }, { Query: `SELECT to_timestamp('2000 + + JUN', 'YYYY MON');`, Expected: []sql.Row{{"2000-06-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('2000 -10', 'YYYY TZH');`, Expected: []sql.Row{{"2000-01-01 03:30:00-06:30"}}, }, { Query: `SELECT to_timestamp('2000 -10', 'YYYY TZH');`, Expected: []sql.Row{{"1999-12-31 07:30:00-06:30"}}, }, { Query: `SELECT to_timestamp('2005527', 'YYYYIWID');`, ExpectedErr: "invalid combination of date conventions", }, { Query: `SELECT to_timestamp('19971', 'YYYYMMDD');`, ExpectedErr: `source string too short for "MM" formatting field`, }, { Query: `SELECT to_timestamp('19971)24', 'YYYYMMDD');`, ExpectedErr: `invalid value "1)" for "MM"`, }, { Query: `SELECT to_timestamp('Friday 1-January-1999', 'DY DD MON YYYY');`, ExpectedErr: `invalid value "da" for "DD"`, }, { Query: `SELECT to_timestamp('Fri 1-January-1999', 'DY DD MON YYYY');`, ExpectedErr: `invalid value "uary" for "YYYY"`, }, { Query: `SELECT to_timestamp('Fri 1-Jan-1999', 'DY DD MON YYYY');`, Expected: []sql.Row{{"1999-01-01 00:00:00-06:30"}}, }, { Query: `SELECT to_timestamp('1997-11-Jan-16', 'YYYY-MM-Mon-DD');`, ExpectedErr: `conflicting values for "Mon" field in formatting string`, }, { Query: `SELECT to_timestamp('199711xy', 'YYYYMMDD');`, ExpectedErr: `invalid value "xy" for "DD"`, }, { Query: `SELECT to_timestamp('10000000000', 'FMYYYY');`, ExpectedErr: `value for "YYYY" in source string is out of range`, }, { Query: `SELECT to_timestamp('2016-06-13 25:00:00', 'YYYY-MM-DD HH24:MI:SS');`, ExpectedErr: `date/time field value out of range`, }, { Query: `SELECT to_timestamp('2016-06-13 15:60:00', 'YYYY-MM-DD HH24:MI:SS');`, ExpectedErr: `date/time field value out of range`, }, { Query: `SELECT to_timestamp('2016-06-13 15:50:60', 'YYYY-MM-DD HH24:MI:SS');`, ExpectedErr: `date/time field value out of range`, }, { Query: `SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2016-06-13 15:50:55-06:30"}}, }, { Query: `SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH:MI:SS');`, ExpectedErr: `hour "15" is invalid for the 12-hour clock`, }, { Query: `SELECT to_timestamp('2016-13-01 15:50:55', 'YYYY-MM-DD HH24:MI:SS');`, ExpectedErr: `date/time field value out of range`, }, { Query: `SELECT to_timestamp('2016-02-30 15:50:55', 'YYYY-MM-DD HH24:MI:SS');`, ExpectedErr: `date/time field value out of range`, }, { Query: `SELECT to_timestamp('2016-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS');`, Expected: []sql.Row{{"2016-02-29 15:50:55-06:30"}}, }, { Query: `SELECT to_timestamp('2015-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS');`, ExpectedErr: `date/time field value out of range`, }, { Query: `SELECT to_timestamp('2015-02-11 86000', 'YYYY-MM-DD SSSS');`, Expected: []sql.Row{{"2015-02-11 23:53:20-06:30"}}, }, { Query: `SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSS');`, ExpectedErr: `date/time field value out of range`, }, { Query: `SELECT to_timestamp('2015-02-11 86000', 'YYYY-MM-DD SSSSS');`, Expected: []sql.Row{{"2015-02-11 23:53:20-06:30"}}, }, { Query: `SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSSS');`, ExpectedErr: `date/time field value out of range`, }, { Query: `SET timezone to '+06:30';`, }, }, }, { Name: "current_time and now functions", Assertions: []ScriptTestAssertion{ { Query: `SELECT now()::timetz::text = current_time()::text;`, Expected: []sql.Row{{"t"}}, }, { Skip: true, // TODO: support precision Query: `SELECT now()::timetz(4)::text = current_time(5)::text;`, Expected: []sql.Row{{false}}, }, { Query: `SELECT length(to_char(current_date + 'now'::timetz, 'HH24:MI:SS.USTZH:TZM'));`, Expected: []sql.Row{{int32(21)}}, }, { Query: `SELECT length('now'::timetz::text) > length('now'::time::text);`, // Direct ::text casts trim trailing zeros from microseconds, making the length variable. Expected: []sql.Row{{"t"}}, }, { Query: `SELECT length(to_char('now'::time, 'HH24:MI:SS.US'));`, Expected: []sql.Row{{int32(15)}}, }, }, }, { Name: "make_timestamp", Assertions: []ScriptTestAssertion{ { Query: `SELECT make_timestamp(2014, 12, 28, 6, 30, 45.887);`, Expected: []sql.Row{{"2014-12-28 06:30:45.887"}}, }, { Query: `SELECT make_timestamp(-44, 3, 15, 12, 30, 15);`, Expected: []sql.Row{{"0044-03-15 12:30:15 BC"}}, }, { Query: `SELECT make_timestamp(-1, 3, 15, 12, 30, 15);`, Expected: []sql.Row{{"0001-03-15 12:30:15 BC"}}, }, { Query: `select make_timestamp(0, 7, 15, 12, 30, 15);`, ExpectedErr: `date field value out of range`, }, { Query: `select make_timestamp(2000, 0, 15, 12, 30, 15);`, ExpectedErr: `date field value out of range`, }, { Query: `select make_timestamp(2000, 7, 32, 12, 30, 15);`, ExpectedErr: `date field value out of range`, }, { Query: `select make_timestamp(2000, 7, 15, 25, 30, 15);`, ExpectedErr: `time field value out of range`, }, { Query: `select make_timestamp(2000, 7, 15, 2, 61, 15);`, ExpectedErr: `time field value out of range`, }, { Query: `select make_timestamp(2000, 7, 15, 25, 30, 61);`, ExpectedErr: `time field value out of range`, }, }, }, { Name: "make_timestamptz", SetUpScript: []string{ `SET timezone = '+06:30'`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT make_timestamptz(2014, 12, 28, 6, 30, 45.887);`, Expected: []sql.Row{{"2014-12-28 06:30:45.887-06:30"}}, }, { Query: `SELECT make_timestamptz(-44, 3, 15, 12, 30, 15);`, Expected: []sql.Row{{"0044-03-15 12:30:15-06:30 BC"}}, }, { Query: `SELECT make_timestamptz(-1, 3, 15, 12, 30, 15);`, Expected: []sql.Row{{"0001-03-15 12:30:15-06:30 BC"}}, }, { Query: `select make_timestamptz(0, 7, 15, 12, 30, 15);`, ExpectedErr: `date field value out of range`, }, { Query: `SELECT make_timestamptz(1910, 12, 24, 0, 0, 0, 'Nehwon/Lankhmar');`, ExpectedErr: `time zone "Nehwon/Lankhmar" not recognized`, }, { Query: `SELECT make_timestamptz(1881, 12, 10, 0, 0, 0, 'Europe/Paris') AT TIME ZONE 'UTC';`, Expected: []sql.Row{{"1881-12-09 23:50:39"}}, }, { Query: `SELECT make_timestamptz(2008, 12, 10, 10, 10, 10, 'EST');`, Expected: []sql.Row{{"2008-12-10 08:40:10-06:30"}}, }, }, }, { Name: "date_bin", Assertions: []ScriptTestAssertion{ { Query: `SELECT date_bin('5 min'::interval, timestamp '2020-02-01 01:01:01', timestamp '2020-02-01 00:02:30');`, Expected: []sql.Row{{"2020-02-01 00:57:30"}}, }, { Query: `SELECT date_bin('5 months'::interval, timestamp '2020-02-01 01:01:01', timestamp '2001-01-01');`, ExpectedErr: `timestamps cannot be binned into intervals containing months or years`, }, { Query: `SELECT date_bin('0 days'::interval, timestamp '1970-01-01 01:00:00' , timestamp '1970-01-01 00:00:00');`, ExpectedErr: `stride must be greater than zero`, }, }, }, }) } func TestStringFunction(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "use name type for text type input", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT ascii('name'::name)`, Expected: []sql.Row{{110}}, }, { Query: "SELECT bit_length('name'::name);", Expected: []sql.Row{{32}}, }, { Query: "SELECT btrim(' name '::name);", Expected: []sql.Row{{"name"}}, }, { Query: "SELECT initcap('name'::name);", Expected: []sql.Row{{"Name"}}, }, { Query: "SELECT left('name'::name, 2);", Expected: []sql.Row{{"na"}}, }, { Query: "SELECT length('name'::name);", Expected: []sql.Row{{4}}, }, { Query: "SELECT lower('naMe'::name);", Expected: []sql.Row{{"name"}}, }, { Query: "SELECT lpad('name'::name, 7, '*');", Expected: []sql.Row{{"***name"}}, }, }, }, { Name: "quote_ident", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `select quote_ident('hi"bye');`, Expected: []sql.Row{{`"hi""bye"`}}, }, { Query: `select quote_ident('hi""bye');`, Expected: []sql.Row{{`"hi""""bye"`}}, }, { Query: `select quote_ident('hi"""bye');`, Expected: []sql.Row{{`"hi""""""bye"`}}, }, { Query: `select quote_ident('hi"b"ye');`, Expected: []sql.Row{{`"hi""b""ye"`}}, }, }, }, { Name: "translate", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `select translate('12345', '143', 'ax');`, Expected: []sql.Row{{`a2x5`}}, }, { Query: `select translate('12345', '143', 'axs');`, Expected: []sql.Row{{`a2sx5`}}, }, { Query: `select translate('12345', '143', 'axsl');`, Expected: []sql.Row{{`a2sx5`}}, }, { Query: `select translate('こんにちは', 'ん', 'a');`, Expected: []sql.Row{{`こaにちは`}}, }, }, }, { Name: "substring with integer arg", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT substr('hello', 2)`, Expected: []sql.Row{{"ello"}}, }, { Query: `SELECT substring('hello', 2)`, Expected: []sql.Row{{"ello"}}, }, }, }, { Name: "substring with integer args", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT substr('hello', 2, 3)`, Expected: []sql.Row{{"ell"}}, }, { Query: `SELECT substring('hello', 2, 3)`, Expected: []sql.Row{{"ell"}}, }, }, }, { Name: "substring with integer args, expanded form", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT substr('hello' from 2 for 3)`, ExpectedErr: "syntax error", }, { Query: `SELECT substring('hello' from 2 for 3)`, Expected: []sql.Row{{"ell"}}, }, { Query: `SELECT substr('hello' from 2)`, ExpectedErr: "syntax error", }, { Query: `SELECT substring('hello' from 2)`, Expected: []sql.Row{{"ello"}}, }, { Query: `SELECT substr('hello' for 3)`, ExpectedErr: "syntax error", }, { Query: `SELECT substring('hello' for 3)`, Skip: true, // ERROR: function substring(unknown, bigint, integer) does not exist Expected: []sql.Row{{"hel"}}, }, }, }, { Name: "substring with regex", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: "SELECT substring('hello', 'l+')", Expected: []sql.Row{{"ll"}}, }, { Query: "SELECT substring('hello' FROM 'l+')", Expected: []sql.Row{{"ll"}}, }, { Query: `SELECT substring('hello.' similar 'hello#.' escape '#')`, Skip: true, // syntax error Expected: []sql.Row{{"hello."}}, }, { Query: `SELECT substring('Thomas' similar '%#"o_a#"_' escape '#')`, Skip: true, // syntax error Expected: []sql.Row{{"oma"}}, }, }, }, { Name: "string_agg", SetUpScript: []string{ "CREATE TABLE test (pk INT primary key, v1 INT, v2 TEXT);", "INSERT INTO test VALUES (1, 1, 'a'), (2, 2, 'b'), (3, 3, 'c'), (4, 4, 'd'), (5, 5, 'e');", }, Assertions: []ScriptTestAssertion{ { Query: `SELECT string_agg(v1::text, ',') FROM test;`, Expected: []sql.Row{ {"1,2,3,4,5"}, }, }, { Query: `SELECT string_agg(v2, '|') FROM test;`, Expected: []sql.Row{ {"a|b|c|d|e"}, }, }, { Query: `SELECT STRING_AGG(concat(v1::text, v2), ' * ') FROM test;`, Expected: []sql.Row{ {"1a * 2b * 3c * 4d * 5e"}, }, }, { Skip: true, // can't use expressions for separator because GROUP_CONCAT can't at the moment Query: `SELECT STRING_agg(concat(v1::text, v2), CONCAT(' *', ' ') ORDER BY V1 DESC) FROM test;`, Expected: []sql.Row{ {"5e * 4d * 3c * 2b * 1a"}, }, }, { Query: `SELECT STRING_AGG(v2, '*', v1) FROM test;`, ExpectedErr: "string_agg requires two arguments", }, { Query: `SELECT STRING_AGG(v2) FROM test;`, ExpectedErr: "string_agg requires two arguments", }, { Query: `SELECT STRING_AGG(concat(v1::text, v2), ' * '::text) FROM test;`, Expected: []sql.Row{ {"1a * 2b * 3c * 4d * 5e"}, }, }, { Query: `SELECT STRING_AGG(concat(v1::text, v2), 8::text) FROM test;`, Expected: []sql.Row{ {"1a82b83c84d85e"}, }, }, }, }, { Name: "concat", Assertions: []ScriptTestAssertion{ { // https://github.com/dolthub/doltgresql/issues/2547 Query: `SELECT LENGTH(CONCAT('', NULL, ''));`, Expected: []sql.Row{{0}}, }, { Query: `SELECT CONCAT('a', NULL, 'b');`, Expected: []sql.Row{{"ab"}}, }, { Query: `SELECT CONCAT(1, 2, true);`, Expected: []sql.Row{{"12t"}}, }, { Query: `SELECT CONCAT(1::int2, 2::int4, true::bool, false::text);`, Expected: []sql.Row{{"12tfalse"}}, }, { Query: `SELECT CONCAT('b');`, Expected: []sql.Row{{"b"}}, }, { Query: `SELECT CONCAT(NULL);`, Expected: []sql.Row{{""}}, }, }, }, }) } func TestFormatFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "test to_char", SetUpScript: []string{ `CREATE TABLE TIMESTAMP_TBL (d1 timestamp(2) without time zone);`, `INSERT INTO TIMESTAMP_TBL VALUES ('1997-02-10 17:32:01-0800');`, }, Assertions: []ScriptTestAssertion{ { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'YYYY-MM-DD HH24:MI:SS.MS');`, Expected: []sql.Row{ {"2021-09-15 21:43:56.123"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'HH HH12 HH24 hh hh12 hh24 H h hH Hh');`, Expected: []sql.Row{ {"09 09 21 09 09 21 H h hH Hh"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'MI mi M m');`, Expected: []sql.Row{ {"43 43 M m"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'SS ss S s MS ms Ms mS US us Us uS');`, Expected: []sql.Row{ {"56 56 S s 123 123 Ms mS 123457 123457 Us uS"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'Y,YYY y,yyy YYYY yyyy YYY yyy YY yy Y y');`, Expected: []sql.Row{ {"2,021 2,021 2021 2021 021 021 21 21 1 1"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'MONTH Month month MON Mon mon MM mm Mm mM');`, Expected: []sql.Row{ {"SEPTEMBER September september SEP Sep sep 09 09 Mm mM"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'DAY Day day DDD ddd DY Dy dy DD dd D d');`, Expected: []sql.Row{ {"WEDNESDAY Wednesday wednesday 258 258 WED Wed wed 15 15 4 4"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'DAY Day day DDD ddd DY Dy dy DD dd D d');`, Expected: []sql.Row{ {"WEDNESDAY Wednesday wednesday 258 258 WED Wed wed 15 15 4 4"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'IW iw');`, Expected: []sql.Row{ {"37 37"}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'AM PM am pm A.M. P.M. a.m. p.m.');`, Expected: []sql.Row{ {"PM PM pm pm P.M. P.M. p.m. p.m."}, }, }, { Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'Q q');`, Expected: []sql.Row{ {"3 3"}, }, }, { Query: `SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD SSSS');`, Expected: []sql.Row{ {"2012-12-12 43200"}, }, }, { Query: `SET timezone = '-06:30';`, }, { Query: `SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZH:TZM');`, Expected: []sql.Row{ {"2012-12-12 12:00:00 +06:30"}, }, }, { Query: `SELECT to_char('2012-12-12 12:00 -02:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZH:TZM');`, Expected: []sql.Row{ {"2012-12-12 08:30:00 +06:30"}, }, }, { Query: `SELECT to_char('2012-12-12 12:00 -02:00'::timestamptz, 'TZ');`, Expected: []sql.Row{ {""}, }, }, { Query: `SET timezone = 'UTC';`, }, { Query: `SELECT to_char('2012-12-12 12:00 -02:00'::timestamptz, 'TZ tz');`, Expected: []sql.Row{ {"UTC utc"}, }, }, { Query: `SELECT to_char(d1, 'Y,YYY YYYY YYY YY Y CC Q MM WW DDD DD D J') FROM TIMESTAMP_TBL;`, Expected: []sql.Row{ {"1,997 1997 997 97 7 20 1 02 06 041 10 2 2450490"}, }, }, { Query: `SELECT to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon') FROM TIMESTAMP_TBL;`, Expected: []sql.Row{ {`MONDAY Monday monday MON Mon mon FEBRUARY February february II FEB Feb feb`}, }, }, }, }, }) } func TestUnknownFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "unknown functions", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT unknown_func();`, ExpectedErr: `function: 'unknown_func' not found`, }, }, }, { Name: "Unsupported group_concat syntax", SetUpScript: []string{ "CREATE TABLE x (pk int)", "INSERT INTO x VALUES (1),(2),(3),(4),(NULL)", }, Assertions: []ScriptTestAssertion{ { Query: `SELECT group_concat(pk ORDER BY pk) FROM x;`, ExpectedErr: "is not yet supported", // error message is kind of nonsensical, we just want to make sure there isn't a panic }, }, }, }) } func TestSelectFromFunctions(t *testing.T) { RunScripts(t, []ScriptTest{ { Name: "select * FROM functions", SetUpScript: []string{}, Assertions: []ScriptTestAssertion{ { Query: `SELECT array_to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*')`, Expected: []sql.Row{{"1,2,3,*,5"}}, }, { Query: `SELECT * FROM array_to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*')`, Expected: []sql.Row{{"1,2,3,*,5"}}, }, { Query: `SELECT * FROM array_to_string(ARRAY[37.89, 1.2], '_');`, Expected: []sql.Row{{"37.89_1.2"}}, }, { Query: `SELECT * FROM format_type('text'::regtype, 4);`, Expected: []sql.Row{{"text(4)"}}, }, { Query: `SELECT * from format_type(874938247, 20);`, Expected: []sql.Row{{"???"}}, }, { Query: `SELECT * FROM to_char(timestamp '2021-09-15 21:43:56.123456789', 'IW iw');`, Expected: []sql.Row{ {"37 37"}, }, }, { Query: `SELECT * from format_type('text'::regtype, -1);`, Expected: []sql.Row{ {"text"}, }, }, { Query: `SELECT "left" FROM left('name'::name, 2);`, Expected: []sql.Row{{"na"}}, }, { Query: "SELECT length FROM length('name'::name);", Expected: []sql.Row{{4}}, }, { Query: "SELECT lower FROM lower('naMe'::name);", Expected: []sql.Row{{"name"}}, }, { Query: "SELECT * FROM lpad('name'::name, 7, '*');", Expected: []sql.Row{{"***name"}}, }, }, }, { Name: "test select from dolt_ functions", Skip: true, // need a way for single-row functions to declare a schema like table functions do, maybe just by modeling them as table functions in the first place SetUpScript: []string{ "CREATE TABLE test (pk INT primary key, v1 INT, v2 TEXT);", "INSERT INTO test VALUES (1, 1, 'a'), (2, 2, 'b'), (3, 3, 'c'), (4, 4, 'd'), (5, 5, 'e');", "call dolt_commit('-Am', 'first table');", }, Assertions: []ScriptTestAssertion{ { Query: `select * from dolt_branch('newBranch')`, Expected: []sql.Row{{0}}, }, { Query: `select status from dolt_checkout('newBranch')`, Expected: []sql.Row{{0}}, }, { Query: `insert into test values (6, 6, 'f')`, }, { Query: `select length(commit_hash) > 0 from (select commit_hash from dolt_commit('-Am', 'added f') as result)`, Expected: []sql.Row{{"t"}}, }, { Query: "select dolt_checkout('main')", Expected: []sql.Row{{0}}, }, { Query: `select fast_forward, conflicts from dolt_merge('newBranch')`, Expected: []sql.Row{{"t", 0}}, }, }, }, }) } func TestSetReturningFunctions(t *testing.T) { RunScripts( t, []ScriptTest{ { Name: "generate_series", Assertions: []ScriptTestAssertion{ { Query: `SELECT generate_series(1,3)`, Expected: []sql.Row{{1}, {2}, {3}}, }, { Query: `SELECT generate_series(1,6,2)`, Expected: []sql.Row{{1}, {3}, {5}}, }, { Query: `SELECT generate_series(1::int4,6::int4,0::int4)`, ExpectedErr: "step size cannot equal zero", }, { Query: `SELECT generate_series(6,1,-2)`, Expected: []sql.Row{{6}, {4}, {2}}, }, { Query: `SELECT generate_series(1.5,6,2)`, Expected: []sql.Row{{Numeric("1.5")}, {Numeric("3.5")}, {Numeric("5.5")}}, }, { Query: `SELECT generate_series(1::int8,6::int8,0::int8)`, ExpectedErr: "step size cannot equal zero", }, { Query: `SELECT generate_series(6,2.2,-2)`, Expected: []sql.Row{{Numeric("6")}, {Numeric("4")}}, }, { Query: `SELECT generate_series('2008-03-01 00:00'::timestamp,'2008-03-02 12:00', '10 hours');`, Expected: []sql.Row{ {"2008-03-01 00:00:00"}, {"2008-03-01 10:00:00"}, {"2008-03-01 20:00:00"}, {"2008-03-02 06:00:00"}, }, }, { Query: `SELECT generate_series('2008-03-02 12:00'::timestamp,'2008-03-01 00:00'::timestamp, '-10 hours');`, Expected: []sql.Row{ {"2008-03-02 12:00:00"}, {"2008-03-02 02:00:00"}, {"2008-03-01 16:00:00"}, {"2008-03-01 06:00:00"}, }, }, }, }, { Name: "generate_series as table function", Assertions: []ScriptTestAssertion{ { Query: `SELECT * FROM generate_series(1,3)`, Expected: []sql.Row{{1}, {2}, {3}}, }, { Query: `SELECT * FROM generate_series(1,6,2)`, Expected: []sql.Row{{1}, {3}, {5}}, }, { Query: `SELECT * FROM generate_series(-100::numeric, 100::numeric, 0::numeric);`, ExpectedErr: `step size cannot equal zero`, }, { Query: `SELECT * FROM generate_series('2008-03-02 12:00'::timestamp,'2008-03-01 00:00'::timestamp, '-10 hours'::interval);`, Expected: []sql.Row{ {"2008-03-02 12:00:00"}, {"2008-03-02 02:00:00"}, {"2008-03-01 16:00:00"}, {"2008-03-01 06:00:00"}, }, }, { Query: `select * from generate_series('2020-01-01 00:00'::timestamp, '2020-01-02 03:00'::timestamp, '0 hour'::interval);`, ExpectedErr: `step size cannot equal zero`, }, { Skip: true, // TODO: cannot cast unknown to interval, but this should work Query: `SELECT * FROM generate_series('2008-03-02 12:00'::timestamp,'2008-03-01 00:00'::timestamp, '-10 hours');`, Expected: []sql.Row{ {"2008-03-02 12:00:00"}, {"2008-03-02 02:00:00"}, {"2008-03-01 16:00:00"}, {"2008-03-01 06:00:00"}, }, }, { Query: `SELECT generate_series('1.2'::numeric,2.4)`, Expected: []sql.Row{{Numeric("1.2")}, {Numeric("2.2")}}, }, { Query: `SELECT generate_series('1.2'::numeric,1.4,0.1)`, Expected: []sql.Row{{Numeric("1.2")}, {Numeric("1.3")}, {Numeric("1.4")}}, }, { Query: `SELECT generate_series('Nan'::numeric,1.4,0.1)`, ExpectedErr: `start value cannot be NaN`, }, { Query: `SELECT generate_series('NaN'::numeric,1.4)`, ExpectedErr: `start value cannot be NaN`, }, { Query: `SELECT generate_series('1.2'::numeric,'Infinity',0.1)`, ExpectedErr: `stop value cannot be infinity`, }, { Query: `SELECT generate_series('1.2'::numeric,'-Infinity')`, ExpectedErr: `stop value cannot be infinity`, }, { Query: `SELECT generate_series('1.2'::numeric,1.4,'NAN')`, ExpectedErr: `step value cannot be NaN`, }, }, }, { Name: "nested generate_series", // Nested SRF expressions cause an infinite loop, skipped in regression tests. // Challenging to fix with the current expression eval architecture and very marginal as a use case. Skip: true, Assertions: []ScriptTestAssertion{ { Query: `SELECT generate_series(1, generate_series(1, 3))`, Expected: []sql.Row{ {1}, {1}, {2}, {1}, {2}, {3}, }, }, }, }, { Name: "limit, offset, sort", Assertions: []ScriptTestAssertion{ { Query: `SELECT a, generate_series(1,2) FROM (VALUES(1),(2),(3)) r(a) LIMIT 2 OFFSET 2;`, Expected: []sql.Row{ {2, 1}, {2, 2}, }, }, { Query: `SELECT a, generate_series(1,2) FROM (VALUES(1),(2),(3)) r(a) ORDER BY 1;`, Expected: []sql.Row{ {1, 1}, {1, 2}, {2, 1}, {2, 2}, {3, 1}, {3, 2}, }, }, }, }, { Name: "generate_series with table", SetUpScript: []string{ "CREATE TABLE t1 (pk INT primary key, v1 INT);", "INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3);", }, Assertions: []ScriptTestAssertion{ { Query: `SELECT generate_series(1,3), pk from t1`, Expected: []sql.Row{ {1, 1}, {2, 1}, {3, 1}, {1, 2}, {2, 2}, {3, 2}, {1, 3}, {2, 3}, {3, 3}, }, }, { Query: `SELECT generate_series(1,3) + pk, pk from t1`, Expected: []sql.Row{ {2, 1}, {3, 1}, {4, 1}, {3, 2}, {4, 2}, {5, 2}, {4, 3}, {5, 3}, {6, 3}, }, }, }, }, { Name: "set returning function as table function: generate_series", Skip: true, // select * from functions does not work yet Assertions: []ScriptTestAssertion{ { Query: `select * from generate_series(1,3)`, Expected: []sql.Row{{1}, {2}, {3}}, }, { Query: `select sum(null::int4) from generate_series(1,3);`, Expected: []sql.Row{{nil}}, }, { Query: `SELECT * from generate_series('2008-03-01 00:00'::timestamp,'2008-03-02 12:00', '10 hours');`, Expected: []sql.Row{ {"2008-03-01 00:00:00"}, {"2008-03-01 10:00:00"}, {"2008-03-01 20:00:00"}, {"2008-03-02 06:00:00"}, }, }, }, }, { Name: "generate_subscripts", SetUpScript: []string{ "CREATE TABLE t1 (pk INT primary key, v1 INT[]);", "INSERT INTO t1 VALUES (1, ARRAY[1, 2, 3]), (2, ARRAY[4, 5]), (3, NULL);", }, Assertions: []ScriptTestAssertion{ { Query: "select generate_subscripts(v1, 1) from t1 where pk = 1", Expected: []sql.Row{ {1}, {2}, {3}, }, }, { Query: "select generate_subscripts(v1, 1) + 100 from t1 where pk = 1", Expected: []sql.Row{ {101}, {102}, {103}, }, }, { Query: "select generate_subscripts(v1, 1) from t1 where pk = 3", Expected: []sql.Row{}, }, { Query: "select generate_subscripts(v1, 1), v1 from t1", Expected: []sql.Row{ {1, "{1,2,3}"}, {2, "{1,2,3}"}, {3, "{1,2,3}"}, {1, "{4,5}"}, {2, "{4,5}"}, }, }, }, }, { Name: "generate_subscripts with join", SetUpScript: []string{ "CREATE TABLE t1 (a INT[]);", "CREATE TABLE t2 (b int[]);", "INSERT INTO t1 VALUES (ARRAY[1]), (ARRAY[1, 2, 3])", "INSERT INTO t2 VALUES (ARRAY[9,10])", }, Assertions: []ScriptTestAssertion{ { Query: "select generate_subscripts(a, 1), a, generate_subscripts(b, 1), b from t1, t2;", Expected: []sql.Row{ {1, "{1}", 1, "{9,10}"}, {nil, "{1}", 2, "{9,10}"}, {1, "{1,2,3}", 1, "{9,10}"}, {2, "{1,2,3}", 2, "{9,10}"}, {3, "{1,2,3}", nil, "{9,10}"}, }, }, }, }, { Name: "generate_subscripts and generate_series combined", SetUpScript: []string{ "CREATE TABLE t1 (a INT[]);", "INSERT INTO t1 VALUES (ARRAY[1, 2, 3]), (ARRAY[4, 5]);", }, Assertions: []ScriptTestAssertion{ { Query: "select generate_subscripts(a, 1), a, generate_series(1,4) from t1;", Expected: []sql.Row{ {1, "{1,2,3}", 1}, {2, "{1,2,3}", 2}, {3, "{1,2,3}", 3}, {nil, "{1,2,3}", 4}, {1, "{4,5}", 1}, {2, "{4,5}", 2}, {nil, "{4,5}", 3}, {nil, "{4,5}", 4}, }, }, }, }, { Name: "generate_subscripts on 0-indexed array types", Assertions: []ScriptTestAssertion{ { Query: "SELECT generate_subscripts('1 2 3'::int2vector, 1);", Expected: []sql.Row{ {0}, {1}, {2}, }, }, { Query: "SELECT generate_subscripts('1 2 3'::oidvector, 1);", Expected: []sql.Row{ {0}, {1}, {2}, }, }, }, }, { Name: "set generation with other func calls", SetUpScript: []string{ "CREATE sequence test_seq START WITH 1 INCREMENT BY 3;", }, Assertions: []ScriptTestAssertion{ { Query: `SELECT generate_series(1, 5), nextval('test_seq')`, Expected: []sql.Row{ {1, 1}, {2, 4}, {3, 7}, {4, 10}, {5, 13}, }, }, }, }, { Name: "generate_series as table function and projection", Assertions: []ScriptTestAssertion{ { Query: `SELECT *, unnest(ARRAY['cat', 'dog', 'bird']) AS animal FROM generate_series(1, 3);`, Expected: []sql.Row{{1, "cat"}, {1, "dog"}, {1, "bird"}, {2, "cat"}, {2, "dog"}, {2, "bird"}, {3, "cat"}, {3, "dog"}, {3, "bird"}}, }, }, }, { Name: "insert with set returning function", SetUpScript: []string{ "create table hash_parted (a int, b int, c int);", }, Assertions: []ScriptTestAssertion{ { Query: `insert into hash_parted values(0, generate_series(1,3), generate_series(5,8));`, Expected: []sql.Row{}, }, { Query: `insert into hash_parted values(0, generate_series(11,12), generate_series(51,54)), (1, generate_series(1,3), generate_series(5,8));`, ExpectedErr: `set-returning functions are not allowed in VALUES`, }, { Query: `select * from hash_parted;`, Expected: []sql.Row{{0, 1, 5}, {0, 2, 6}, {0, 3, 7}, {0, nil, 8}}, }, }, }, }, ) }