Skip to content

Commit

Permalink
builtins: add pg_sequence_last_value builtin
Browse files Browse the repository at this point in the history
Release note (sql change): Added the pg_sequence_last_value builtin
function, which returns the last value generated by the sequence.
  • Loading branch information
rafiss committed Jul 7, 2023
1 parent 3bb32b8 commit 17b36b7
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3635,6 +3635,8 @@ table. Returns an error if validation fails.</p>
</span></td><td>Stable</td></tr>
<tr><td><a name="pg_relation_is_updatable"></a><code>pg_relation_is_updatable(reloid: oid, include_triggers: <a href="bool.html">bool</a>) &rarr; int4</code></td><td><span class="funcdesc"><p>Returns the update events the relation supports.</p>
</span></td><td>Stable</td></tr>
<tr><td><a name="pg_sequence_last_value"></a><code>pg_sequence_last_value(sequence_oid: oid) &rarr; <a href="int.html">int</a></code></td><td><span class="funcdesc"><p>Returns the last value generated by a sequence, or NULL if the sequence has not been used yet.</p>
</span></td><td>Volatile</td></tr>
<tr><td><a name="pg_sleep"></a><code>pg_sleep(seconds: <a href="float.html">float</a>) &rarr; <a href="bool.html">bool</a></code></td><td><span class="funcdesc"><p>pg_sleep makes the current session’s process sleep until seconds seconds have elapsed. seconds is a value of type double precision, so fractional-second delays can be specified.</p>
</span></td><td>Volatile</td></tr>
<tr><td><a name="pg_table_is_visible"></a><code>pg_table_is_visible(oid: oid) &rarr; <a href="bool.html">bool</a></code></td><td><span class="funcdesc"><p>Returns whether the table with the given OID belongs to one of the schemas on the search path.</p>
Expand Down
7 changes: 7 additions & 0 deletions pkg/sql/faketreeeval/evalctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ func (so *DummySequenceOperators) GetLatestValueInSessionForSequenceByID(
return 0, errors.WithStack(errSequenceOperators)
}

// GetLastSequenceValueByID implements the eval.SequenceOperators interface.
func (so *DummySequenceOperators) GetLastSequenceValueByID(
ctx context.Context, seqID uint32,
) (int64, bool, error) {
return 0, false, errors.WithStack(errSequenceOperators)
}

// SetSequenceValueByID implements the eval.SequenceOperators interface.
func (so *DummySequenceOperators) SetSequenceValueByID(
ctx context.Context, seqID uint32, newVal int64, isCalled bool,
Expand Down
7 changes: 7 additions & 0 deletions pkg/sql/importer/import_table_creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,13 @@ func (so *importSequenceOperators) GetLatestValueInSessionForSequenceByID(
return 0, errSequenceOperators
}

// GetLastSequenceValueByID implements the eval.SequenceOperators interface.
func (so *importSequenceOperators) GetLastSequenceValueByID(
ctx context.Context, seqID uint32,
) (int64, bool, error) {
return 0, false, errSequenceOperators
}

// SetSequenceValueByID implements the eval.SequenceOperators interface.
func (so *importSequenceOperators) SetSequenceValueByID(
ctx context.Context, seqID uint32, newVal int64, isCalled bool,
Expand Down
68 changes: 68 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/sequences
Original file line number Diff line number Diff line change
Expand Up @@ -2325,3 +2325,71 @@ SELECT nextval('customer_seq_check_cache_and_bounds_3')

statement error pgcode 2200H pq: nextval\(\): reached maximum value of sequence "customer_seq_check_cache_and_bounds_3" \(12\)
SELECT nextval('customer_seq_check_cache_and_bounds_3')

subtest pg_sequence_last_value

statement ok
CREATE SEQUENCE seq_test_last_value START WITH 2 INCREMENT BY 3

query I
SELECT pg_sequence_last_value('seq_test_last_value'::regclass)
----
NULL

query I
SELECT nextval('seq_test_last_value')
----
2

query I
SELECT pg_sequence_last_value('seq_test_last_value'::regclass)
----
2

query I
SELECT nextval('seq_test_last_value')
----
5

query I
SELECT pg_sequence_last_value('seq_test_last_value'::regclass)
----
5

statement ok
CREATE SEQUENCE seq_test_last_value_cached START WITH 1 INCREMENT BY 2 CACHE 5

query I
SELECT pg_sequence_last_value('seq_test_last_value_cached'::regclass)
----
NULL

query I
SELECT nextval('seq_test_last_value_cached')
----
1

# Matches PostgreSQL behavior for cached sequences.
query I
SELECT pg_sequence_last_value('seq_test_last_value_cached'::regclass)
----
9

query I
SELECT nextval('seq_test_last_value_cached')
----
3

# Matches PostgreSQL behavior for cached sequences.
query I
SELECT pg_sequence_last_value('seq_test_last_value_cached'::regclass)
----
9

statement error relation \"\[123456\]\" does not exist
SELECT pg_sequence_last_value(123456)

statement error pgcode 42809 \"test.public.multiple_seq_test_tbl\" is not a sequence
SELECT pg_sequence_last_value('multiple_seq_test_tbl'::regclass)

subtest end
1 change: 1 addition & 0 deletions pkg/sql/sem/builtins/fixed_oids.go
Original file line number Diff line number Diff line change
Expand Up @@ -2427,6 +2427,7 @@ var builtinOidsArray = []string{
2454: `crdb_internal.sstable_metrics(node_id: int, store_id: int, start_key: bytes, end_key: bytes) -> tuple{int AS node_id,, int AS store_id, int AS level, int AS file_num, jsonb AS metrics}`,
2455: `crdb_internal.repair_catalog_corruption(descriptor_id: int, corruption: string) -> bool`,
2456: `crdb_internal.merge_aggregated_stmt_metadata(input: jsonb[]) -> jsonb`,
2457: `pg_sequence_last_value(sequence_oid: oid) -> int`,
}

var builtinOidsBySignature map[string]oid.Oid
Expand Down
24 changes: 24 additions & 0 deletions pkg/sql/sem/builtins/pg_builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,30 @@ var pgBuiltins = map[string]builtinDefinition{
},
),

"pg_sequence_last_value": makeBuiltin(
tree.FunctionProperties{
Category: builtinconstants.CategorySequences,
},
tree.Overload{
Types: tree.ParamTypes{{Name: "sequence_oid", Typ: types.Oid}},
ReturnType: tree.FixedReturnType(types.Int),
Fn: func(ctx context.Context, evalCtx *eval.Context, args tree.Datums) (tree.Datum, error) {
seqOid := tree.MustBeDOid(args[0])

value, wasCalled, err := evalCtx.Sequence.GetLastSequenceValueByID(ctx, uint32(seqOid.Oid))
if err != nil {
return nil, err
}
if !wasCalled {
return tree.DNull, nil
}
return tree.NewDInt(tree.DInt(value)), nil
},
Info: "Returns the last value generated by a sequence, or NULL if the sequence has not been used yet.",
Volatility: volatility.Volatile,
},
),

// pg_my_temp_schema returns the OID of session's temporary schema, or 0 if
// none.
// https://www.postgresql.org/docs/11/functions-info.html
Expand Down
5 changes: 5 additions & 0 deletions pkg/sql/sem/eval/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,11 @@ type SequenceOperators interface {
// `newVal + seqOpts.Increment`.
// Takes in a sequence ID rather than a name, unlike SetSequenceValue.
SetSequenceValueByID(ctx context.Context, seqID uint32, newVal int64, isCalled bool) error

// GetLastSequenceValueByID returns the last value returned by the sequence,
// not specific to any session. It also returns a flag to indicate if the
// sequence has been called before.
GetLastSequenceValueByID(ctx context.Context, seqID uint32) (value int64, wasCalled bool, err error)
}

// ChangefeedState is used to track progress and checkpointing for sinkless/core changefeeds.
Expand Down
26 changes: 26 additions & 0 deletions pkg/sql/sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,32 @@ func (p *planner) SetSequenceValueByID(
return nil
}

// GetLastSequenceValueByID implements the eval.SequenceOperators interface.
func (p *planner) GetLastSequenceValueByID(
ctx context.Context, seqID uint32,
) (val int64, wasCalled bool, err error) {
descriptor, err := p.Descriptors().ByIDWithLeased(p.txn).WithoutNonPublic().Get().Table(ctx, descpb.ID(seqID))
if err != nil {
return 0, false, err
}
seqName, err := p.getQualifiedTableName(ctx, descriptor)
if err != nil {
return 0, false, err
}
if !descriptor.IsSequence() {
return 0, false, sqlerrors.NewWrongObjectTypeError(seqName, "sequence")
}
val, err = getSequenceValueFromDesc(ctx, p.txn, p.execCfg.Codec, descriptor)
if err != nil {
return 0, false, err
}

// Before using for the first time, sequenceValue will be:
// opts.Start - opts.Increment.
opts := descriptor.GetSequenceOpts()
return val, val != opts.Start-opts.Increment, nil
}

// MakeSequenceKeyVal returns the key and value of a sequence being set
// with newVal.
func MakeSequenceKeyVal(
Expand Down

0 comments on commit 17b36b7

Please sign in to comment.