1#include "duckdb/function/scalar/sequence_functions.hpp"
2
3#include "duckdb/catalog/catalog.hpp"
4#include "duckdb/catalog/catalog_entry/sequence_catalog_entry.hpp"
5#include "duckdb/common/exception.hpp"
6#include "duckdb/common/vector_operations/vector_operations.hpp"
7#include "duckdb/execution/expression_executor.hpp"
8#include "duckdb/catalog/catalog.hpp"
9#include "duckdb/planner/expression/bound_function_expression.hpp"
10#include "duckdb/transaction/transaction.hpp"
11#include "duckdb/common/vector_operations/unary_executor.hpp"
12
13using namespace std;
14
15namespace duckdb {
16
17struct NextvalBindData : public FunctionData {
18 //! The client context for the function call
19 ClientContext &context;
20 //! The sequence to use for the nextval computation; only if
21 SequenceCatalogEntry *sequence;
22
23 NextvalBindData(ClientContext &context, SequenceCatalogEntry *sequence) : context(context), sequence(sequence) {
24 }
25
26 unique_ptr<FunctionData> Copy() override {
27 return make_unique<NextvalBindData>(context, sequence);
28 }
29};
30
31static int64_t next_sequence_value(Transaction &transaction, SequenceCatalogEntry *seq) {
32 lock_guard<mutex> seqlock(seq->lock);
33 int64_t result;
34 if (seq->cycle) {
35 result = seq->counter;
36 seq->counter += seq->increment;
37 ;
38 if (result < seq->min_value) {
39 result = seq->max_value;
40 seq->counter = seq->max_value + seq->increment;
41 } else if (result > seq->max_value) {
42 result = seq->min_value;
43 seq->counter = seq->min_value + seq->increment;
44 }
45 } else {
46 result = seq->counter;
47 seq->counter += seq->increment;
48 if (result < seq->min_value) {
49 throw SequenceException("nextval: reached minimum value of sequence \"%s\" (%lld)", seq->name.c_str(),
50 seq->min_value);
51 }
52 if (result > seq->max_value) {
53 throw SequenceException("nextval: reached maximum value of sequence \"%s\" (%lld)", seq->name.c_str(),
54 seq->max_value);
55 }
56 }
57 seq->usage_count++;
58 transaction.sequence_usage[seq] = SequenceValue(seq->usage_count, seq->counter);
59 return result;
60}
61
62static void nextval_function(DataChunk &args, ExpressionState &state, Vector &result) {
63 auto &func_expr = (BoundFunctionExpression &)state.expr;
64 auto &info = (NextvalBindData &)*func_expr.bind_info;
65 assert(args.column_count() == 1 && args.data[0].type == TypeId::VARCHAR);
66 auto &input = args.data[0];
67
68 auto &transaction = Transaction::GetTransaction(info.context);
69 if (info.sequence) {
70 // sequence to use is hard coded
71 // increment the sequence
72 result.vector_type = VectorType::FLAT_VECTOR;
73 auto result_data = FlatVector::GetData<int64_t>(result);
74 for (idx_t i = 0; i < args.size(); i++) {
75 // get the next value from the sequence
76 result_data[i] = next_sequence_value(transaction, info.sequence);
77 }
78 } else {
79 // sequence to use comes from the input
80 UnaryExecutor::Execute<string_t, int64_t, true>(input, result, args.size(), [&](string_t value) {
81 string schema, seq;
82 string seqname = value.GetString();
83 Catalog::ParseRangeVar(seqname, schema, seq);
84 // fetch the sequence from the catalog
85 auto sequence = Catalog::GetCatalog(info.context).GetEntry<SequenceCatalogEntry>(info.context, schema, seq);
86 // finally get the next value from the sequence
87 return next_sequence_value(transaction, sequence);
88 });
89 }
90}
91
92static unique_ptr<FunctionData> nextval_bind(BoundFunctionExpression &expr, ClientContext &context) {
93 SequenceCatalogEntry *sequence = nullptr;
94 if (expr.children[0]->IsFoldable()) {
95 string schema, seq;
96 // parameter to nextval function is a foldable constant
97 // evaluate the constant and perform the catalog lookup already
98 Value seqname = ExpressionExecutor::EvaluateScalar(*expr.children[0]);
99 if (!seqname.is_null) {
100 assert(seqname.type == TypeId::VARCHAR);
101 Catalog::ParseRangeVar(seqname.str_value, schema, seq);
102 sequence = Catalog::GetCatalog(context).GetEntry<SequenceCatalogEntry>(context, schema, seq);
103 }
104 }
105 return make_unique<NextvalBindData>(context, sequence);
106}
107
108static void nextval_dependency(BoundFunctionExpression &expr, unordered_set<CatalogEntry *> &dependencies) {
109 auto &info = (NextvalBindData &)*expr.bind_info;
110 if (info.sequence) {
111 dependencies.insert(info.sequence);
112 }
113}
114
115void NextvalFun::RegisterFunction(BuiltinFunctions &set) {
116 set.AddFunction(ScalarFunction("nextval", {SQLType::VARCHAR}, SQLType::BIGINT, nextval_function, true, nextval_bind,
117 nextval_dependency));
118}
119
120} // namespace duckdb
121