Repository: dolthub/doltgresql
Pull request #1390: support for array subscript
Author: login: zachmu
Created: 2025-04-15T00:12:05Z | Merged: 2025-04-15T16:01:33Z

Only supports one-dimension subscripts and no slices


Diff:
diff --git a/server/ast/expr.go b/server/ast/expr.go
index 2899eccfaf..f4d8075877 100644
--- a/server/ast/expr.go
+++ b/server/ast/expr.go
@@ -607,7 +607,26 @@ func nodeExpr(ctx *Context, node tree.Expr) (vitess.Expr, error) {
 		// TODO: figure out if I can delete this
 		return nil, errors.Errorf("this should probably be deleted (internal error, IndexedVar)")
 	case *tree.IndirectionExpr:
-		return nil, errors.Errorf("subscripts are not yet supported")
+		childExpr, err := nodeExpr(ctx, node.Expr)
+		if err != nil {
+			return nil, err
+		}
+
+		if len(node.Indirection) > 1 {
+			return nil, errors.Errorf("multi dimensional array subscripts are not yet supported")
+		} else if node.Indirection[0].Slice {
+			return nil, errors.Errorf("slice subscripts are not yet supported")
+		}
+
+		indexExpr, err := nodeExpr(ctx, node.Indirection[0].Begin)
+		if err != nil {
+			return nil, err
+		}
+
+		return vitess.InjectedExpr{
+			Expression: &pgexprs.Subscript{},
+			Children:   vitess.Exprs{childExpr, indexExpr},
+		}, nil
 	case *tree.IsNotNullExpr:
 		expr, err := nodeExpr(ctx, node.Expr)
 		if err != nil {
diff --git a/server/expression/subscript.go b/server/expression/subscript.go
new file mode 100755
index 0000000000..08961018d8
--- /dev/null
+++ b/server/expression/subscript.go
@@ -0,0 +1,134 @@
+// Copyright 2025 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 expression
+
+import (
+	"fmt"
+
+	"github.com/dolthub/go-mysql-server/sql"
+	vitess "github.com/dolthub/vitess/go/vt/sqlparser"
+
+	"github.com/dolthub/doltgresql/server/types"
+)
+
+// Subscript represents a subscript expression, e.g. `a[1]`.
+type Subscript struct {
+	Child sql.Expression
+	Index sql.Expression
+}
+
+var _ vitess.Injectable = (*Subscript)(nil)
+var _ sql.Expression = (*Subscript)(nil)
+
+// NewSubscript creates a new Subscript expression.
+func NewSubscript(child, index sql.Expression) *Subscript {
+	return &Subscript{
+		Child: child,
+		Index: index,
+	}
+}
+
+// Resolved implements the sql.Expression interface.
+func (s Subscript) Resolved() bool {
+	return s.Child.Resolved() && s.Index.Resolved()
+}
+
+// String implements the sql.Expression interface.
+func (s Subscript) String() string {
+	return fmt.Sprintf("%s[%s]", s.Child, s.Index)
+}
+
+// Type implements the sql.Expression interface.
+func (s Subscript) Type() sql.Type {
+	dt, ok := s.Child.Type().(*types.DoltgresType)
+	if !ok {
+		panic(fmt.Sprintf("unexpected type %T for subscript", s.Child.Type()))
+	}
+	return dt.ArrayBaseType()
+}
+
+// IsNullable implements the sql.Expression interface.
+func (s Subscript) IsNullable() bool {
+	return true
+}
+
+// Eval implements the sql.Expression interface.
+func (s Subscript) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
+	childVal, err := s.Child.Eval(ctx, row)
+	if err != nil {
+		return nil, err
+	}
+	if childVal == nil {
+		return nil, nil
+	}
+
+	indexVal, err := s.Index.Eval(ctx, row)
+	if err != nil {
+		return nil, err
+	}
+	if indexVal == nil {
+		return nil, nil
+	}
+
+	switch child := childVal.(type) {
+	case []interface{}:
+		index, ok := indexVal.(int32)
+		if !ok {
+			converted, _, err := types.Int32.Convert(ctx, indexVal)
+			if err != nil {
+				return nil, err
+			}
+			index = converted.(int32)
+		}
+
+		// subscripts are 1-based
+		if index < 1 || int(index) > len(child) {
+			return nil, nil
+		}
+		return child[index-1], nil
+	default:
+		return nil, fmt.Errorf("unsupported type %T for subscript", child)
+	}
+}
+
+// Children implements the sql.Expression interface.
+func (s Subscript) Children() []sql.Expression {
+	return []sql.Expression{s.Child, s.Index}
+}
+
+// WithChildren implements the sql.Expression interface.
+func (s Subscript) WithChildren(children ...sql.Expression) (sql.Expression, error) {
+	if len(children) != 2 {
+		return nil, fmt.Errorf("expected 2 children, got %d", len(children))
+	}
+	return NewSubscript(children[0], children[1]), nil
+}
+
+// WithResolvedChildren implements the vitess.Injectable interface.
+func (s Subscript) WithResolvedChildren(children []any) (any, error) {
+	if len(children) != 2 {
+		return nil, fmt.Errorf("expected 2 children, got %d", len(children))
+	}
+	child, ok := children[0].(sql.Expression)
+	if !ok {
+		return nil, fmt.Errorf("expected child to be an expression but has type `%T`", children[0])
+	}
+	index, ok := children[1].(sql.Expression)
+	if !ok {
+		return nil, fmt.Errorf("expected index to be an expression but has type `%T`", children[1])
+	}
+
+	return NewSubscript(child, index), nil
+}
diff --git a/testing/go/expressions_test.go b/testing/go/expressions_test.go
index 894a08d59e..2e2f50add5 100644
--- a/testing/go/expressions_test.go
+++ b/testing/go/expressions_test.go
@@ -21,7 +21,7 @@ import (
 	"github.com/dolthub/go-mysql-server/sql"
 )
 
-func TestExpressions(t *testing.T) {
+func TestIn(t *testing.T) {
 	RunScriptsWithoutNormalization(t, []ScriptTest{
 		anyTests("ANY"),
 		anyTests("SOME"),
@@ -292,3 +292,78 @@ func TestBinaryLogic(t *testing.T) {
 		},
 	})
 }
+
+// Note that our parser is more forgiving of array subscripts than the actual Postgres parser.
+// We can handle this: SELECT ARRAY[1, 2, 3][1]
+// But postgres requires: SELECT (ARRAY[1, 2, 3])[1]
+func TestSubscript(t *testing.T) {
+	RunScripts(t, []ScriptTest{
+		{
+			Name: "array literal",
+			Assertions: []ScriptTestAssertion{
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][1];`,
+					Expected: []sql.Row{{1}},
+				},
+				{
+					Query:    `SELECT (ARRAY[1, 2, 3])[3];`,
+					Expected: []sql.Row{{3}},
+				},
+				{
+					Query:    `SELECT (ARRAY[1, 2, 3])[1+1];`,
+					Expected: []sql.Row{{2}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][0];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][4];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][null];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY['a', 'b', 'c'][2];`,
+					Expected: []sql.Row{{"b"}},
+				},
+				{
+					Query:       `SELECT ARRAY[1, 2, 3][1:3];`,
+					ExpectedErr: "not yet supported",
+				},
+				{
+					Query:       `SELECT ARRAY[1, 2, 3]['abc'];`,
+					ExpectedErr: "integer: unhandled type: string",
+				},
+			},
+		},
+		{
+			Name: "array column",
+			SetUpScript: []string{
+				`CREATE TABLE test (id INT, arr INT[]);`,
+				`INSERT INTO test VALUES (1, ARRAY[1, 2, 3]), (2, ARRAY[4, 5, 6]);`,
+			},
+			Assertions: []ScriptTestAssertion{
+				{
+					Query:    `SELECT arr[2] FROM test order by 1;`,
+					Expected: []sql.Row{{2}, {5}},
+				},
+			},
+		},
+		{
+			Name: "array subquery",
+			SetUpScript: []string{
+				"CREATE TABLE test (id INT);",
+				"INSERT INTO test VALUES (1), (2), (3);",
+			},
+			Assertions: []ScriptTestAssertion{
+				{
+					Query:    `SELECT (array(select id from test order by 1))[2]`,
+					Expected: []sql.Row{{2}},
+				},
+			},
+		},
+	})
+}
diff --git a/testing/go/functions_test.go b/testing/go/functions_test.go
index eef6284020..70aa685919 100644
--- a/testing/go/functions_test.go
+++ b/testing/go/functions_test.go
@@ -2310,5 +2310,39 @@ func TestSelectFromFunctions(t *testing.T) {
 				},
 			},
 		},
+		{
+			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}},
+				},
+			},
+		},
 	})
 }


Reviews:
user: login: Hydrocharged
body: LGTM
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-15T13:48:14Z
user: login: zachmu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-15T15:59:38Z
user: login: zachmu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-15T16:00:06Z

Review comments:
diff_hunk: @@ -292,3 +292,78 @@ func TestBinaryLogic(t *testing.T) {
 		},
 	})
 }
+
+// Note that our parser is more forgiving of array subscripts than the actual Postgres parser.
+// We can handle this: SELECT ARRAY[1, 2, 3][1]
+// But postgres requires: SELECT (ARRAY[1, 2, 3])[1]
+func TestSubscript(t *testing.T) {
+	RunScripts(t, []ScriptTest{
+		{
+			Name: "array literal",
+			Assertions: []ScriptTestAssertion{
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][1];`,
+					Expected: []sql.Row{{1}},
+				},
+				{
+					Query:    `SELECT (ARRAY[1, 2, 3])[3];`,
+					Expected: []sql.Row{{3}},
+				},
+				{
+					Query:    `SELECT (ARRAY[1, 2, 3])[1+1];`,
+					Expected: []sql.Row{{2}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][0];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][4];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][null];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY['a', 'b', 'c'][2];`,
+					Expected: []sql.Row{{"b"}},
+				},
+				{
+					Query:       `SELECT ARRAY[1, 2, 3][1:3];`,
+					ExpectedErr: "not yet supported",
+				},
+				{
+					Query:       `SELECT ARRAY[1, 2, 3]['abc'];`,
+					ExpectedErr: "integer: unhandled type: string",
path: testing/go/expressions_test.go
user: login: Hydrocharged
body: This seems like a strange error message, but I guess Postgres returns:
```
invalid input syntax for type integer: "abc"
```
created_at: 2025-04-15T13:44:17Z
author_association: COLLABORATOR
diff_hunk: @@ -607,7 +607,26 @@ func nodeExpr(ctx *Context, node tree.Expr) (vitess.Expr, error) {
 		// TODO: figure out if I can delete this
 		return nil, errors.Errorf("this should probably be deleted (internal error, IndexedVar)")
 	case *tree.IndirectionExpr:
-		return nil, errors.Errorf("subscripts are not yet supported")
+		childExpr, err := nodeExpr(ctx, node.Expr)
+		if err != nil {
+			return nil, err
+		}
+
+		if len(node.Indirection) > 1 {
+			return nil, errors.Errorf("multi dimensional array subscripts are not yet supported")
+		} else if node.Indirection[0].Slice {
path: server/ast/expr.go
user: login: Hydrocharged
body: I'm assuming that it's technically not possible for `len(node.Indirection) == 0`, but it _would_ panic here if that were ever the case in the future.
created_at: 2025-04-15T13:45:33Z
author_association: COLLABORATOR
diff_hunk: @@ -607,7 +607,26 @@ func nodeExpr(ctx *Context, node tree.Expr) (vitess.Expr, error) {
 		// TODO: figure out if I can delete this
 		return nil, errors.Errorf("this should probably be deleted (internal error, IndexedVar)")
 	case *tree.IndirectionExpr:
-		return nil, errors.Errorf("subscripts are not yet supported")
+		childExpr, err := nodeExpr(ctx, node.Expr)
+		if err != nil {
+			return nil, err
+		}
+
+		if len(node.Indirection) > 1 {
+			return nil, errors.Errorf("multi dimensional array subscripts are not yet supported")
+		} else if node.Indirection[0].Slice {
path: server/ast/expr.go
user: login: zachmu
body: Right, the parser can't produce that.
created_at: 2025-04-15T15:59:38Z
author_association: MEMBER
diff_hunk: @@ -292,3 +292,78 @@ func TestBinaryLogic(t *testing.T) {
 		},
 	})
 }
+
+// Note that our parser is more forgiving of array subscripts than the actual Postgres parser.
+// We can handle this: SELECT ARRAY[1, 2, 3][1]
+// But postgres requires: SELECT (ARRAY[1, 2, 3])[1]
+func TestSubscript(t *testing.T) {
+	RunScripts(t, []ScriptTest{
+		{
+			Name: "array literal",
+			Assertions: []ScriptTestAssertion{
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][1];`,
+					Expected: []sql.Row{{1}},
+				},
+				{
+					Query:    `SELECT (ARRAY[1, 2, 3])[3];`,
+					Expected: []sql.Row{{3}},
+				},
+				{
+					Query:    `SELECT (ARRAY[1, 2, 3])[1+1];`,
+					Expected: []sql.Row{{2}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][0];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][4];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY[1, 2, 3][null];`,
+					Expected: []sql.Row{{nil}},
+				},
+				{
+					Query:    `SELECT ARRAY['a', 'b', 'c'][2];`,
+					Expected: []sql.Row{{"b"}},
+				},
+				{
+					Query:       `SELECT ARRAY[1, 2, 3][1:3];`,
+					ExpectedErr: "not yet supported",
+				},
+				{
+					Query:       `SELECT ARRAY[1, 2, 3]['abc'];`,
+					ExpectedErr: "integer: unhandled type: string",
path: testing/go/expressions_test.go
user: login: zachmu
body: Matching postgres's error messages for these more closely will be a good project for the future.
created_at: 2025-04-15T16:00:06Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-15T00:22:55Z
body: <!-- go-run-output-sysbench -->
|   | Main | PR |   |
| --- | --- | --- | --- |
| covering_index_scan_postgres | 343.01/s | 337.30/s | -1.7% |
| index_join_postgres | 150.98/s | 149.94/s | -0.7% |
| index_join_scan_postgres | 181.98/s | 182.76/s | +0.4% |
| index_scan_postgres | 12.44/s | 12.38/s | -0.5% |
| oltp_point_select | 2641.88/s | 2604.02/s | -1.5% |
| oltp_read_only | 1808.78/s | 1786.89/s | -1.3% |
| select_random_points | 112.30/s | 114.18/s | +1.6% |
| select_random_ranges | 129.79/s | 129.25/s | -0.5% |
| table_scan_postgres | 11.76/s | 11.69/s | -0.6% |
| types_table_scan_postgres | 5.46/s | 5.38/s | -1.5% |
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-15T00:45:54Z
body: <!-- go-run-output -->
|   | Main | PR |
| --- | --- | --- |
| Total | 42090 | 42090 |
| Successful | 16316 | 16344 |
| Failures | 25774 | 25746 |
| Partial Successes[^1] | 5522 | 5532 |

|   | Main | PR |
| --- | --- | --- |
| Successful | 38.7646% | 38.8311% |
| Failures | 61.2354% | 61.1689% |

## ${\color{lightgreen}Progressions (28)}$
### domain
```
QUERY: create domain orderedpair as int[2] check (value[1] < value[2]);
```
```
QUERY: create temp table op (f1 orderedpair);
```
### jsonb
```
QUERY: select ('123'::jsonb)[NULL];
```
```
QUERY: select ('{"a": 1}'::jsonb)[NULL];
```
### opr_sanity
```
QUERY: SELECT DISTINCT p1.proargtypes[4]::regtype, p2.proargtypes[4]::regtype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
    p1.prosrc = p2.prosrc AND
    p1.prolang = 12 AND p2.prolang = 12 AND
    p1.prokind != 'a' AND p2.prokind != 'a' AND
    (p1.proargtypes[4] < p2.proargtypes[4])
ORDER BY 1, 2;
```
```
QUERY: SELECT DISTINCT p1.proargtypes[5]::regtype, p2.proargtypes[5]::regtype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
    p1.prosrc = p2.prosrc AND
    p1.prolang = 12 AND p2.prolang = 12 AND
    p1.prokind != 'a' AND p2.prokind != 'a' AND
    (p1.proargtypes[5] < p2.proargtypes[5])
ORDER BY 1, 2;
```
```
QUERY: SELECT DISTINCT p1.proargtypes[6]::regtype, p2.proargtypes[6]::regtype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
    p1.prosrc = p2.prosrc AND
    p1.prolang = 12 AND p2.prolang = 12 AND
    p1.prokind != 'a' AND p2.prokind != 'a' AND
    (p1.proargtypes[6] < p2.proargtypes[6])
ORDER BY 1, 2;
```
```
QUERY: SELECT DISTINCT p1.proargtypes[7]::regtype, p2.proargtypes[7]::regtype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
    p1.prosrc = p2.prosrc AND
    p1.prolang = 12 AND p2.prolang = 12 AND
    p1.prokind != 'a' AND p2.prokind != 'a' AND
    (p1.proargtypes[7] < p2.proargtypes[7])
ORDER BY 1, 2;
```
```
QUERY: SELECT p1.oid, p1.proname, p2.oid, p2.proname
FROM pg_proc AS p1, pg_proc AS p2
WHERE p2.oid = p1.prosupport AND
    (p2.prorettype != 'internal'::regtype OR p2.proretset OR p2.pronargs != 1
     OR p2.proargtypes[0] != 'internal'::regtype);
```
```
QUERY: SELECT c.*
FROM pg_cast c, pg_proc p
WHERE c.castfunc = p.oid AND
    ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR
     (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype));
```
```
QUERY: SELECT p.oid, p.proname, c.oid, c.conname
FROM pg_proc p, pg_conversion c
WHERE p.oid = c.conproc AND
    (p.prorettype != 'int4'::regtype OR p.proretset OR
     p.pronargs != 6 OR
     p.proargtypes[0] != 'int4'::regtype OR
     p.proargtypes[1] != 'int4'::regtype OR
     p.proargtypes[2] != 'cstring'::regtype OR
     p.proargtypes[3] != 'internal'::regtype OR
     p.proargtypes[4] != 'int4'::regtype OR
     p.proargtypes[5] != 'bool'::regtype);
```
```
QUERY: SELECT o1.oid, o1.oprname, p2.oid, p2.proname
FROM pg_operator AS o1, pg_proc AS p2
WHERE o1.oprrest = p2.oid AND
    (o1.oprresult != 'bool'::regtype OR
     p2.prorettype != 'float8'::regtype OR p2.proretset OR
     p2.pronargs != 4 OR
     p2.proargtypes[0] != 'internal'::regtype OR
     p2.proargtypes[1] != 'oid'::regtype OR
     p2.proargtypes[2] != 'internal'::regtype OR
     p2.proargtypes[3] != 'int4'::regtype);
```
```
QUERY: SELECT o1.oid, o1.oprname, p2.oid, p2.proname
FROM pg_operator AS o1, pg_proc AS p2
WHERE o1.oprjoin = p2.oid AND
    (o1.oprkind != 'b' OR o1.oprresult != 'bool'::regtype OR
     p2.prorettype != 'float8'::regtype OR p2.proretset OR
     p2.pronargs != 5 OR
     p2.proargtypes[0] != 'internal'::regtype OR
     p2.proargtypes[1] != 'oid'::regtype OR
     p2.proargtypes[2] != 'internal'::regtype OR
     p2.proargtypes[3] != 'int2'::regtype OR
     p2.proargtypes[4] != 'internal'::regtype);
```
```
QUERY: SELECT a.aggfnoid, p.proname
FROM pg_aggregate as a, pg_proc as p
WHERE a.aggserialfn = p.oid AND
    (p.prorettype != 'bytea'::regtype OR p.pronargs != 1 OR
     p.proargtypes[0] != 'internal'::regtype OR
     NOT p.proisstrict);
```
```
QUERY: SELECT a.aggfnoid, p.proname
FROM pg_aggregate as a, pg_proc as p
WHERE a.aggdeserialfn = p.oid AND
    (p.prorettype != 'internal'::regtype OR p.pronargs != 2 OR
     p.proargtypes[0] != 'bytea'::regtype OR
     p.proargtypes[1] != 'internal'::regtype OR
     NOT p.proisstrict);
```
```
QUERY: SELECT a.aggfnoid::oid, o.oid
FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p
WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND
    (oprkind != 'b' OR oprresult != 'boolean'::regtype
     OR oprleft != p.proargtypes[0] OR oprright != p.proargtypes[0]);
```
### plpgsql
```
QUERY: create domain orderedarray as int[2]
  constraint sorted check (value[1] < value[2]);
```
```
QUERY: create function testoa(x1 int, x2 int, x3 int) returns orderedarray
language plpgsql as $$
declare res orderedarray;
begin
  res := array[x1, x2];
  res[2] := x3;
  return res;
end$$;
```
```
QUERY: drop function testoa(x1 int, x2 int, x3 int);
```
### subselect
```
QUERY: SELECT (SELECT ARRAY[1,2,3])[1];
```
```
QUERY: SELECT ((SELECT ARRAY[1,2,3]))[2];
```
```
QUERY: SELECT (((SELECT ARRAY[1,2,3])))[3];
```
### type_sanity
```
QUERY: SELECT t1.oid, t1.typname, p1.oid, p1.proname
FROM pg_type AS t1, pg_proc AS p1
WHERE t1.typinput = p1.oid AND NOT
    ((p1.pronargs = 1 AND p1.proargtypes[0] = 'cstring'::regtype) OR
     (p1.pronargs = 2 AND p1.proargtypes[0] = 'cstring'::regtype AND
      p1.proargtypes[1] = 'oid'::regtype) OR
     (p1.pronargs = 3 AND p1.proargtypes[0] = 'cstring'::regtype AND
      p1.proargtypes[1] = 'oid'::regtype AND
      p1.proargtypes[2] = 'int4'::regtype));
```
```
QUERY: SELECT t1.oid, t1.typname, p1.oid, p1.proname
FROM pg_type AS t1, pg_proc AS p1
WHERE t1.typreceive = p1.oid AND NOT
    ((p1.pronargs = 1 AND p1.proargtypes[0] = 'internal'::regtype) OR
     (p1.pronargs = 2 AND p1.proargtypes[0] = 'internal'::regtype AND
      p1.proargtypes[1] = 'oid'::regtype) OR
     (p1.pronargs = 3 AND p1.proargtypes[0] = 'internal'::regtype AND
      p1.proargtypes[1] = 'oid'::regtype AND
      p1.proargtypes[2] = 'int4'::regtype));
```
```
QUERY: SELECT t1.oid, t1.typname, p1.oid, p1.proname
FROM pg_type AS t1, pg_proc AS p1
WHERE t1.typmodout = p1.oid AND NOT
    (p1.pronargs = 1 AND
     p1.proargtypes[0] = 'int4'::regtype AND
     p1.prorettype = 'cstring'::regtype AND NOT p1.proretset);
```
```
QUERY: SELECT t1.oid, t1.typname, p1.oid, p1.proname
FROM pg_type AS t1, pg_proc AS p1
WHERE t1.typanalyze = p1.oid AND NOT
    (p1.pronargs = 1 AND
     p1.proargtypes[0] = 'internal'::regtype AND
     p1.prorettype = 'bool'::regtype AND NOT p1.proretset);
```
```
QUERY: SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngcanonical
WHERE pronargs != 1 OR proargtypes[0] != rngtypid OR prorettype != rngtypid;
```
```
QUERY: SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngsubdiff
WHERE pronargs != 2
    OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
    OR prorettype != 'pg_catalog.float8'::regtype;
```
[^1]: These are tests that we're marking as `Successful`, however they do not match the expected output in some way. This is due to small differences, such as different wording on the error messages, or the column names being incorrect while the data itself is correct.
author_association: CONTRIBUTOR