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 | |
13 | using namespace std; |
14 | |
15 | namespace duckdb { |
16 | |
17 | struct 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 | |
31 | static 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 | |
62 | static 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 | |
92 | static 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 | |
108 | static 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 | |
115 | void 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 | |