| 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 | |