Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate nullable value from subselect statements #2275

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

ryu-ichiroh
Copy link

@ryu-ichiroh ryu-ichiroh commented May 9, 2023

Hi maintainers.

Change subselect statements containing WHERE/HAVING clauses to be NULL.
Resolves: #1208

@ryu-ichiroh ryu-ichiroh force-pushed the nullable-subselect branch 3 times, most recently from 03ea5bc to c99d5f6 Compare May 9, 2023 03:01
@ryu-ichiroh ryu-ichiroh marked this pull request as ready for review May 9, 2023 03:02
@ryu-ichiroh ryu-ichiroh changed the title Generate nullable subselect statements Generate nullable value from subselect statements May 9, 2023
@kyleconroy kyleconroy added the triage New issues that hasn't been reviewed label Jun 8, 2023
@ryu-ichiroh
Copy link
Author

👀

@kyleconroy
Copy link
Collaborator

@ryu-ichiroh Could you please rebase and regenerate your test output with 1.19.1?

I am a bit concerned about this being a backwards incompatible change, but am leaning towards accepting this as subselects that can return NULL can't be used today without COALESCE checking.

@ryu-ichiroh
Copy link
Author

ryu-ichiroh commented Jul 30, 2023

Could you please rebase and regenerate your test output with 1.19.1?

Sure.

@ryu-ichiroh
Copy link
Author

Could you please rebase and regenerate your test output with 1.19.1?

I've finished.
ce1b4c5

Copy link
Author

@ryu-ichiroh ryu-ichiroh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me have more time. I'll improve.

internal/compiler/output_columns.go Outdated Show resolved Hide resolved
@ryu-ichiroh ryu-ichiroh force-pushed the nullable-subselect branch 2 times, most recently from 093ea55 to ccf2267 Compare August 3, 2023 00:37
Copy link
Author

@ryu-ichiroh ryu-ichiroh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kyleconroy
I've finished to fix all. Could you please review this again?

if !(first.IsFuncCall && strings.EqualFold(first.FuncName, "count")) {
first.NotNull = false
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've researched about cases a subselect returns null.

A column of no rows returns null. but if count() return 0.

According to above documentation, total() also returns 0 in sqlite.
Other aggregate functions returns null.

Comment on lines +13 to +20
const countRowsEmptyTable = `-- name: CountRowsEmptyTable :many
SELECT a, (SELECT count(a) FROM empty) as "count" FROM foo
`

type CountRowsEmptyTableRow struct {
A int32
Count int64
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

count() returns non-null.

Comment on lines +44 to +51
const firstRowFromEmptyTable = `-- name: FirstRowFromEmptyTable :many
SELECT a, (SELECT a FROM empty limit 1) as "first" FROM foo
`

type FirstRowFromEmptyTableRow struct {
A int32
First sql.NullInt32
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When retrieving the first row, it returns null.

Comment on lines 24 to 30

func (q *Queries) BarExists(ctx context.Context, id int64) (int64, error) {
func (q *Queries) BarExists(ctx context.Context, id int64) (sql.NullInt64, error) {
row := q.db.QueryRowContext(ctx, barExists, id)
var column_1 int64
var column_1 sql.NullInt64
err := row.Scan(&column_1)
return column_1, err
}
Copy link
Author

@ryu-ichiroh ryu-ichiroh Aug 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-- name: BarExists :one
SELECT
    EXISTS (
        SELECT
            1
        FROM
            bar
        where
            id = ?
    )

This is a regression. 😞
I understand that EXISTS doesn't return null. so It should be generated as non-null.
There are no tests of select_exists in MySQL, I think the same issue occur.
It doesn't occurred the issue in MySQL, because it is parsed ast.SubLink.
This is the issue specific to SQLite only.

I displayed the ast, but I couldn't find EXISTS clause.
Do you have any idea?

Expand to see AST.

&ast.SelectStmt{
  DistinctClause: (*ast.List)(nil),
  IntoClause:     (*ast.IntoClause)(nil),
  TargetList:     &ast.List{
    Items: []ast.Node{
      &ast.ResTarget{
        Name:        (*string)(nil),
        Indirection: (*ast.List)(nil),
        Val:         &ast.SelectStmt{
          DistinctClause: (*ast.List)(nil),
          IntoClause:     (*ast.IntoClause)(nil),
          TargetList:     &ast.List{
            Items: []ast.Node{
              &ast.ResTarget{
                Name:        (*string)(nil),
                Indirection: (*ast.List)(nil),
                Val:         &ast.A_Const{
                  Val: &ast.Integer{
                    Ival: 1,
                  },
                  Location: 134,
                },
                Location: 134,
              },
            },
          },
          FromClause: &ast.List{
            Items: []ast.Node{
              &ast.RangeVar{
                Catalogname:    (*string)(nil),
                Schemaname:     (*string)(nil),
                Relname:        &"bar",
                Inh:            false,
                Relpersistence: 0x00,
                Alias:          (*ast.Alias)(nil),
                Location:       161,
              },
            },
          },
          WhereClause: &ast.A_Expr{
            Kind: 0x0,
            Name: &ast.List{
              Items: []ast.Node{
                &ast.String{
                  Str: "=",
                },
              },
            },
            Lexpr: &ast.ColumnRef{
              Name:   "",
              Fields: &ast.List{
                Items: []ast.Node{
                  &ast.String{
                    Str: "id",
                  },
                },
              },
              Location: 191,
            },
            Rexpr: &ast.ParamRef{
              Number:   1,
              Location: 196,
              Dollar:   false,
            },
            Location: 0,
          },
          GroupClause: &ast.List{
            Items: []ast.Node{},
          },
          HavingClause: nil,
          WindowClause: &ast.List{
            Items: []ast.Node{},
          },
          ValuesLists: &ast.List{
            Items: []ast.Node{},
          },
          SortClause:    (*ast.List)(nil),
          LimitOffset:   nil,
          LimitCount:    nil,
          LockingClause: (*ast.List)(nil),
          WithClause:    &ast.WithClause{
            Ctes: &ast.List{
              Items: []ast.Node{},
            },
            Recursive: false,
            Location:  0,
          },
          Op:   0x0,
          All:  false,
          Larg: (*ast.SelectStmt)(nil),
          Rarg: (*ast.SelectStmt)(nil),
        },
        Location: 98,
      },
    },
  },
  FromClause: &ast.List{
    Items: []ast.Node{},
  },
  WhereClause: nil,
  GroupClause: &ast.List{
    Items: []ast.Node{},
  },
  HavingClause: nil,
  WindowClause: &ast.List{
    Items: []ast.Node{},
  },
  ValuesLists: &ast.List{
    Items: []ast.Node{},
  },
  SortClause:    (*ast.List)(nil),
  LimitOffset:   nil,
  LimitCount:    nil,
  LockingClause: (*ast.List)(nil),
  WithClause:    &ast.WithClause{
    Ctes: &ast.List{
      Items: []ast.Node{},
    },
    Recursive: false,
    Location:  0,
  },
  Op:   0x0,
  All:  false,
  Larg: (*ast.SelectStmt)(nil),
  Rarg: (*ast.SelectStmt)(nil),
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage New issues that hasn't been reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Subselected value should be nullable
2 participants