duckdb 0.8.2-dev2399.0 → 0.8.2-dev2669.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/binding.gyp +1 -0
  2. package/package.json +1 -1
  3. package/src/duckdb/extension/icu/icu-datepart.cpp +3 -3
  4. package/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp +1 -1
  5. package/src/duckdb/src/catalog/default/default_functions.cpp +5 -0
  6. package/src/duckdb/src/common/enum_util.cpp +35 -1
  7. package/src/duckdb/src/common/http_state.cpp +78 -0
  8. package/src/duckdb/src/core_functions/function_list.cpp +2 -2
  9. package/src/duckdb/src/core_functions/scalar/list/array_slice.cpp +314 -82
  10. package/src/duckdb/src/execution/expression_executor/execute_parameter.cpp +2 -2
  11. package/src/duckdb/src/execution/index/art/art.cpp +43 -31
  12. package/src/duckdb/src/execution/index/art/leaf.cpp +47 -33
  13. package/src/duckdb/src/execution/index/art/node.cpp +31 -24
  14. package/src/duckdb/src/execution/index/art/prefix.cpp +100 -16
  15. package/src/duckdb/src/execution/operator/schema/physical_create_index.cpp +54 -31
  16. package/src/duckdb/src/execution/physical_plan/plan_create_index.cpp +32 -15
  17. package/src/duckdb/src/function/table/arrow/arrow_duck_schema.cpp +57 -0
  18. package/src/duckdb/src/function/table/arrow.cpp +95 -92
  19. package/src/duckdb/src/function/table/arrow_conversion.cpp +45 -68
  20. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  21. package/src/duckdb/src/include/duckdb/common/case_insensitive_map.hpp +1 -0
  22. package/src/duckdb/src/include/duckdb/common/enum_util.hpp +8 -0
  23. package/src/duckdb/src/include/duckdb/common/helper.hpp +8 -3
  24. package/src/duckdb/src/include/duckdb/common/http_state.hpp +61 -28
  25. package/src/duckdb/src/include/duckdb/common/types/value.hpp +4 -1
  26. package/src/duckdb/src/include/duckdb/core_functions/scalar/list_functions.hpp +4 -4
  27. package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +7 -5
  28. package/src/duckdb/src/include/duckdb/execution/index/art/leaf.hpp +6 -6
  29. package/src/duckdb/src/include/duckdb/execution/index/art/node.hpp +6 -0
  30. package/src/duckdb/src/include/duckdb/execution/index/art/prefix.hpp +9 -11
  31. package/src/duckdb/src/include/duckdb/execution/operator/schema/physical_create_index.hpp +8 -1
  32. package/src/duckdb/src/include/duckdb/function/table/arrow/arrow_duck_schema.hpp +99 -0
  33. package/src/duckdb/src/include/duckdb/function/table/arrow.hpp +6 -36
  34. package/src/duckdb/src/include/duckdb/main/capi/capi_internal.hpp +3 -1
  35. package/src/duckdb/src/include/duckdb/main/client_context.hpp +15 -14
  36. package/src/duckdb/src/include/duckdb/main/prepared_statement.hpp +73 -5
  37. package/src/duckdb/src/include/duckdb/main/prepared_statement_data.hpp +6 -6
  38. package/src/duckdb/src/include/duckdb/parser/expression/operator_expression.hpp +20 -3
  39. package/src/duckdb/src/include/duckdb/parser/expression/parameter_expression.hpp +17 -1
  40. package/src/duckdb/src/include/duckdb/parser/statement/execute_statement.hpp +1 -1
  41. package/src/duckdb/src/include/duckdb/parser/transformer.hpp +5 -3
  42. package/src/duckdb/src/include/duckdb/planner/bound_parameter_map.hpp +2 -1
  43. package/src/duckdb/src/include/duckdb/planner/expression/bound_parameter_data.hpp +20 -5
  44. package/src/duckdb/src/include/duckdb/planner/expression/bound_parameter_expression.hpp +3 -3
  45. package/src/duckdb/src/include/duckdb/planner/planner.hpp +4 -3
  46. package/src/duckdb/src/include/duckdb/storage/object_cache.hpp +1 -1
  47. package/src/duckdb/src/include/duckdb/verification/prepared_statement_verifier.hpp +1 -1
  48. package/src/duckdb/src/include/duckdb.h +16 -0
  49. package/src/duckdb/src/main/capi/pending-c.cpp +6 -0
  50. package/src/duckdb/src/main/capi/prepared-c.cpp +52 -4
  51. package/src/duckdb/src/main/client_context.cpp +27 -17
  52. package/src/duckdb/src/main/client_verify.cpp +17 -0
  53. package/src/duckdb/src/main/extension/extension_helper.cpp +2 -1
  54. package/src/duckdb/src/main/prepared_statement.cpp +38 -11
  55. package/src/duckdb/src/main/prepared_statement_data.cpp +23 -18
  56. package/src/duckdb/src/parser/expression/parameter_expression.cpp +7 -7
  57. package/src/duckdb/src/parser/statement/execute_statement.cpp +2 -2
  58. package/src/duckdb/src/parser/transform/expression/transform_array_access.cpp +13 -4
  59. package/src/duckdb/src/parser/transform/expression/transform_param_ref.cpp +45 -26
  60. package/src/duckdb/src/parser/transform/statement/transform_prepare.cpp +28 -6
  61. package/src/duckdb/src/parser/transformer.cpp +27 -9
  62. package/src/duckdb/src/planner/binder/expression/bind_parameter_expression.cpp +10 -10
  63. package/src/duckdb/src/planner/binder/statement/bind_execute.cpp +13 -7
  64. package/src/duckdb/src/planner/expression/bound_parameter_expression.cpp +13 -13
  65. package/src/duckdb/src/planner/planner.cpp +7 -6
  66. package/src/duckdb/src/storage/checkpoint_manager.cpp +1 -1
  67. package/src/duckdb/src/storage/serialization/serialize_expression.cpp +3 -3
  68. package/src/duckdb/src/storage/serialization/serialize_parsed_expression.cpp +2 -2
  69. package/src/duckdb/src/verification/prepared_statement_verifier.cpp +16 -11
  70. package/src/duckdb/third_party/libpg_query/include/nodes/parsenodes.hpp +1 -0
  71. package/src/duckdb/third_party/libpg_query/src_backend_parser_gram.cpp +12855 -12282
  72. package/src/duckdb/ub_src_common.cpp +2 -0
  73. package/src/duckdb/ub_src_function_table_arrow.cpp +2 -0
@@ -304,9 +304,9 @@ static bool IsExplainAnalyze(SQLStatement *statement) {
304
304
  return explain.explain_type == ExplainType::EXPLAIN_ANALYZE;
305
305
  }
306
306
 
307
- shared_ptr<PreparedStatementData> ClientContext::CreatePreparedStatement(ClientContextLock &lock, const string &query,
308
- unique_ptr<SQLStatement> statement,
309
- vector<Value> *values) {
307
+ shared_ptr<PreparedStatementData>
308
+ ClientContext::CreatePreparedStatement(ClientContextLock &lock, const string &query, unique_ptr<SQLStatement> statement,
309
+ optional_ptr<case_insensitive_map_t<Value>> values) {
310
310
  StatementType statement_type = statement->type;
311
311
  auto result = make_shared<PreparedStatementData>(statement_type);
312
312
 
@@ -315,8 +315,9 @@ shared_ptr<PreparedStatementData> ClientContext::CreatePreparedStatement(ClientC
315
315
  profiler.StartPhase("planner");
316
316
  Planner planner(*this);
317
317
  if (values) {
318
- for (auto &value : *values) {
319
- planner.parameter_data.emplace_back(value);
318
+ auto &parameter_values = *values;
319
+ for (auto &value : parameter_values) {
320
+ planner.parameter_data.emplace(value);
320
321
  }
321
322
  }
322
323
 
@@ -370,7 +371,7 @@ double ClientContext::GetProgress() {
370
371
 
371
372
  unique_ptr<PendingQueryResult> ClientContext::PendingPreparedStatement(ClientContextLock &lock,
372
373
  shared_ptr<PreparedStatementData> statement_p,
373
- PendingQueryParameters parameters) {
374
+ const PendingQueryParameters &parameters) {
374
375
  D_ASSERT(active_query);
375
376
  auto &statement = *statement_p;
376
377
  if (ValidChecker::IsInvalidated(ActiveTransaction()) && statement.properties.requires_valid_transaction) {
@@ -392,7 +393,14 @@ unique_ptr<PendingQueryResult> ClientContext::PendingPreparedStatement(ClientCon
392
393
  }
393
394
 
394
395
  // bind the bound values before execution
395
- statement.Bind(parameters.parameters ? *parameters.parameters : vector<Value>());
396
+ case_insensitive_map_t<Value> owned_values;
397
+ if (parameters.parameters) {
398
+ auto &params = *parameters.parameters;
399
+ for (auto &val : params) {
400
+ owned_values.emplace(val);
401
+ }
402
+ }
403
+ statement.Bind(std::move(owned_values));
396
404
 
397
405
  active_query->executor = make_uniq<Executor>(*this);
398
406
  auto &executor = *active_query->executor;
@@ -566,7 +574,7 @@ unique_ptr<PreparedStatement> ClientContext::Prepare(const string &query) {
566
574
 
567
575
  unique_ptr<PendingQueryResult> ClientContext::PendingQueryPreparedInternal(ClientContextLock &lock, const string &query,
568
576
  shared_ptr<PreparedStatementData> &prepared,
569
- PendingQueryParameters parameters) {
577
+ const PendingQueryParameters &parameters) {
570
578
  try {
571
579
  InitialCleanup(lock);
572
580
  } catch (const Exception &ex) {
@@ -579,13 +587,13 @@ unique_ptr<PendingQueryResult> ClientContext::PendingQueryPreparedInternal(Clien
579
587
 
580
588
  unique_ptr<PendingQueryResult> ClientContext::PendingQuery(const string &query,
581
589
  shared_ptr<PreparedStatementData> &prepared,
582
- PendingQueryParameters parameters) {
590
+ const PendingQueryParameters &parameters) {
583
591
  auto lock = LockContext();
584
592
  return PendingQueryPreparedInternal(*lock, query, prepared, parameters);
585
593
  }
586
594
 
587
595
  unique_ptr<QueryResult> ClientContext::Execute(const string &query, shared_ptr<PreparedStatementData> &prepared,
588
- PendingQueryParameters parameters) {
596
+ const PendingQueryParameters &parameters) {
589
597
  auto lock = LockContext();
590
598
  auto pending = PendingQueryPreparedInternal(*lock, query, prepared, parameters);
591
599
  if (pending->HasError()) {
@@ -595,7 +603,7 @@ unique_ptr<QueryResult> ClientContext::Execute(const string &query, shared_ptr<P
595
603
  }
596
604
 
597
605
  unique_ptr<QueryResult> ClientContext::Execute(const string &query, shared_ptr<PreparedStatementData> &prepared,
598
- vector<Value> &values, bool allow_stream_result) {
606
+ case_insensitive_map_t<Value> &values, bool allow_stream_result) {
599
607
  PendingQueryParameters parameters;
600
608
  parameters.parameters = &values;
601
609
  parameters.allow_stream_result = allow_stream_result;
@@ -604,10 +612,11 @@ unique_ptr<QueryResult> ClientContext::Execute(const string &query, shared_ptr<P
604
612
 
605
613
  unique_ptr<PendingQueryResult> ClientContext::PendingStatementInternal(ClientContextLock &lock, const string &query,
606
614
  unique_ptr<SQLStatement> statement,
607
- PendingQueryParameters parameters) {
615
+ const PendingQueryParameters &parameters) {
608
616
  // prepare the query for execution
609
617
  auto prepared = CreatePreparedStatement(lock, query, std::move(statement), parameters.parameters);
610
- if (prepared->properties.parameter_count > 0 && !parameters.parameters) {
618
+ idx_t parameter_count = !parameters.parameters ? 0 : parameters.parameters->size();
619
+ if (prepared->properties.parameter_count > 0 && parameter_count == 0) {
611
620
  string error_message = StringUtil::Format("Expected %lld parameters, but none were supplied",
612
621
  prepared->properties.parameter_count);
613
622
  return make_uniq<PendingQueryResult>(PreservedError(error_message));
@@ -640,7 +649,7 @@ bool ClientContext::IsActiveResult(ClientContextLock &lock, BaseQueryResult *res
640
649
 
641
650
  unique_ptr<PendingQueryResult> ClientContext::PendingStatementOrPreparedStatementInternal(
642
651
  ClientContextLock &lock, const string &query, unique_ptr<SQLStatement> statement,
643
- shared_ptr<PreparedStatementData> &prepared, PendingQueryParameters parameters) {
652
+ shared_ptr<PreparedStatementData> &prepared, const PendingQueryParameters &parameters) {
644
653
  // check if we are on AutoCommit. In this case we should start a transaction.
645
654
  if (statement && config.AnyVerification()) {
646
655
  // query verification is enabled
@@ -697,7 +706,7 @@ unique_ptr<PendingQueryResult> ClientContext::PendingStatementOrPreparedStatemen
697
706
 
698
707
  unique_ptr<PendingQueryResult> ClientContext::PendingStatementOrPreparedStatement(
699
708
  ClientContextLock &lock, const string &query, unique_ptr<SQLStatement> statement,
700
- shared_ptr<PreparedStatementData> &prepared, PendingQueryParameters parameters) {
709
+ shared_ptr<PreparedStatementData> &prepared, const PendingQueryParameters &parameters) {
701
710
  unique_ptr<PendingQueryResult> result;
702
711
 
703
712
  try {
@@ -722,7 +731,7 @@ unique_ptr<PendingQueryResult> ClientContext::PendingStatementOrPreparedStatemen
722
731
  if (statement) {
723
732
  result = PendingStatementInternal(lock, query, std::move(statement), parameters);
724
733
  } else {
725
- if (prepared->RequireRebind(*this, *parameters.parameters)) {
734
+ if (prepared->RequireRebind(*this, parameters.parameters)) {
726
735
  // catalog was modified: rebind the statement before execution
727
736
  auto new_prepared =
728
737
  CreatePreparedStatement(lock, query, prepared->unbound_statement->Copy(), parameters.parameters);
@@ -884,7 +893,8 @@ unique_ptr<PendingQueryResult> ClientContext::PendingQuery(unique_ptr<SQLStateme
884
893
 
885
894
  unique_ptr<PendingQueryResult> ClientContext::PendingQueryInternal(ClientContextLock &lock,
886
895
  unique_ptr<SQLStatement> statement,
887
- PendingQueryParameters parameters, bool verify) {
896
+ const PendingQueryParameters &parameters,
897
+ bool verify) {
888
898
  auto query = statement->query;
889
899
  shared_ptr<PreparedStatementData> prepared;
890
900
  if (verify) {
@@ -7,6 +7,20 @@
7
7
 
8
8
  namespace duckdb {
9
9
 
10
+ static void ThrowIfExceptionIsInternal(StatementVerifier &verifier) {
11
+ if (!verifier.materialized_result) {
12
+ return;
13
+ }
14
+ auto &result = *verifier.materialized_result;
15
+ if (!result.HasError()) {
16
+ return;
17
+ }
18
+ auto &error = result.GetErrorObject();
19
+ if (error.Type() == ExceptionType::INTERNAL) {
20
+ error.Throw();
21
+ }
22
+ }
23
+
10
24
  PreservedError ClientContext::VerifyQuery(ClientContextLock &lock, const string &query,
11
25
  unique_ptr<SQLStatement> statement) {
12
26
  D_ASSERT(statement->type == StatementType::SELECT_STATEMENT);
@@ -81,6 +95,9 @@ PreservedError ClientContext::VerifyQuery(ClientContextLock &lock, const string
81
95
  if (!failed) {
82
96
  // PreparedStatementVerifier fails if it runs into a ParameterNotAllowedException, which is OK
83
97
  statement_verifiers.push_back(std::move(prepared_statement_verifier));
98
+ } else {
99
+ // If it does fail, let's make sure it's not an internal exception
100
+ ThrowIfExceptionIsInternal(*prepared_statement_verifier);
84
101
  }
85
102
  } else {
86
103
  if (ValidChecker::IsInvalidated(*db)) {
@@ -180,7 +180,8 @@ ExtensionLoadResult ExtensionHelper::LoadExtensionInternal(DuckDB &db, const std
180
180
  #endif
181
181
 
182
182
  #ifdef DUCKDB_EXTENSIONS_TEST_WITH_LOADABLE
183
- if (!initial_load && StringUtil::Contains(DUCKDB_EXTENSIONS_TEST_WITH_LOADABLE, extension)) {
183
+ // Note: weird comma's are on purpose to do easy string contains on a list of extension names
184
+ if (!initial_load && StringUtil::Contains(DUCKDB_EXTENSIONS_TEST_WITH_LOADABLE, "," + extension + ",")) {
184
185
  Connection con(db);
185
186
  auto result = con.Query((string) "LOAD '" + DUCKDB_EXTENSIONS_BUILD_PATH + "/" + extension + "/" + extension +
186
187
  ".duckdb_extension'");
@@ -6,9 +6,9 @@
6
6
  namespace duckdb {
7
7
 
8
8
  PreparedStatement::PreparedStatement(shared_ptr<ClientContext> context, shared_ptr<PreparedStatementData> data_p,
9
- string query, idx_t n_param, case_insensitive_map_t<idx_t> named_param_pam_p)
9
+ string query, idx_t n_param, case_insensitive_map_t<idx_t> named_param_map_p)
10
10
  : context(std::move(context)), data(std::move(data_p)), query(std::move(query)), success(true), n_param(n_param),
11
- named_param_map(std::move(named_param_pam_p)) {
11
+ named_param_map(std::move(named_param_map_p)) {
12
12
  D_ASSERT(data || !success);
13
13
  }
14
14
 
@@ -56,19 +56,27 @@ const vector<string> &PreparedStatement::GetNames() {
56
56
  return data->names;
57
57
  }
58
58
 
59
- vector<LogicalType> PreparedStatement::GetExpectedParameterTypes() const {
59
+ case_insensitive_map_t<LogicalType> PreparedStatement::GetExpectedParameterTypes() const {
60
60
  D_ASSERT(data);
61
- vector<LogicalType> expected_types(data->value_map.size());
61
+ case_insensitive_map_t<LogicalType> expected_types(data->value_map.size());
62
62
  for (auto &it : data->value_map) {
63
- D_ASSERT(it.first >= 1);
64
- idx_t param_index = it.first - 1;
65
- D_ASSERT(param_index < expected_types.size());
63
+ auto &identifier = it.first;
64
+ D_ASSERT(data->value_map.count(identifier));
66
65
  D_ASSERT(it.second);
67
- expected_types[param_index] = it.second->value.type();
66
+ expected_types[identifier] = it.second->GetValue().type();
68
67
  }
69
68
  return expected_types;
70
69
  }
71
70
 
71
+ unique_ptr<QueryResult> PreparedStatement::Execute(case_insensitive_map_t<Value> &named_values,
72
+ bool allow_stream_result) {
73
+ auto pending = PendingQuery(named_values, allow_stream_result);
74
+ if (pending->HasError()) {
75
+ return make_uniq<MaterializedQueryResult>(pending->GetErrorObject());
76
+ }
77
+ return pending->Execute();
78
+ }
79
+
72
80
  unique_ptr<QueryResult> PreparedStatement::Execute(vector<Value> &values, bool allow_stream_result) {
73
81
  auto pending = PendingQuery(values, allow_stream_result);
74
82
  if (pending->HasError()) {
@@ -78,14 +86,33 @@ unique_ptr<QueryResult> PreparedStatement::Execute(vector<Value> &values, bool a
78
86
  }
79
87
 
80
88
  unique_ptr<PendingQueryResult> PreparedStatement::PendingQuery(vector<Value> &values, bool allow_stream_result) {
89
+ case_insensitive_map_t<Value> named_values;
90
+ for (idx_t i = 0; i < values.size(); i++) {
91
+ auto &val = values[i];
92
+ named_values[std::to_string(i + 1)] = val;
93
+ }
94
+ return PendingQuery(named_values, allow_stream_result);
95
+ }
96
+
97
+ unique_ptr<PendingQueryResult> PreparedStatement::PendingQuery(case_insensitive_map_t<Value> &named_values,
98
+ bool allow_stream_result) {
81
99
  if (!success) {
82
- throw InvalidInputException("Attempting to execute an unsuccessfully prepared statement!");
100
+ auto exception = InvalidInputException("Attempting to execute an unsuccessfully prepared statement!");
101
+ return make_uniq<PendingQueryResult>(PreservedError(exception));
83
102
  }
84
- D_ASSERT(data);
85
103
  PendingQueryParameters parameters;
86
- parameters.parameters = &values;
104
+ parameters.parameters = &named_values;
105
+
106
+ try {
107
+ VerifyParameters(named_values, named_param_map);
108
+ } catch (const Exception &ex) {
109
+ return make_uniq<PendingQueryResult>(PreservedError(ex));
110
+ }
111
+
112
+ D_ASSERT(data);
87
113
  parameters.allow_stream_result = allow_stream_result && data->properties.allow_stream_result;
88
114
  auto result = context->PendingQuery(query, data, parameters);
115
+ // The result should not contain any reference to the 'vector<Value> parameters.parameters'
89
116
  return result;
90
117
  }
91
118
 
@@ -18,8 +18,9 @@ void PreparedStatementData::CheckParameterCount(idx_t parameter_count) {
18
18
  }
19
19
  }
20
20
 
21
- bool PreparedStatementData::RequireRebind(ClientContext &context, const vector<Value> &values) {
22
- CheckParameterCount(values.size());
21
+ bool PreparedStatementData::RequireRebind(ClientContext &context, optional_ptr<case_insensitive_map_t<Value>> values) {
22
+ idx_t count = values ? values->size() : 0;
23
+ CheckParameterCount(count);
23
24
  if (!unbound_statement) {
24
25
  // no unbound statement!? cannot rebind?
25
26
  return false;
@@ -33,52 +34,56 @@ bool PreparedStatementData::RequireRebind(ClientContext &context, const vector<V
33
34
  return true;
34
35
  }
35
36
  for (auto &it : value_map) {
36
- const idx_t i = it.first - 1;
37
- if (values[i].type() != it.second->return_type) {
37
+ auto &identifier = it.first;
38
+ auto lookup = values->find(identifier);
39
+ D_ASSERT(lookup != values->end());
40
+ if (lookup->second.type() != it.second->return_type) {
38
41
  return true;
39
42
  }
40
43
  }
41
44
  return false;
42
45
  }
43
46
 
44
- void PreparedStatementData::Bind(vector<Value> values) {
47
+ void PreparedStatementData::Bind(case_insensitive_map_t<Value> values) {
45
48
  // set parameters
46
49
  D_ASSERT(!unbound_statement || unbound_statement->n_param == properties.parameter_count);
47
50
  CheckParameterCount(values.size());
48
51
 
49
52
  // bind the required values
50
53
  for (auto &it : value_map) {
51
- const idx_t i = it.first - 1;
52
- if (i >= values.size()) {
53
- throw BinderException("Could not find parameter with index %llu", i + 1);
54
+ const string &identifier = it.first;
55
+ auto lookup = values.find(identifier);
56
+ if (lookup == values.end()) {
57
+ throw BinderException("Could not find parameter with identifier %s", identifier);
54
58
  }
55
59
  D_ASSERT(it.second);
56
- if (!values[i].DefaultTryCastAs(it.second->return_type)) {
60
+ auto &value = lookup->second;
61
+ if (!value.DefaultTryCastAs(it.second->return_type)) {
57
62
  throw BinderException(
58
- "Type mismatch for binding parameter with index %llu, expected type %s but got type %s", i + 1,
59
- it.second->return_type.ToString().c_str(), values[i].type().ToString().c_str());
63
+ "Type mismatch for binding parameter with identifier %s, expected type %s but got type %s", identifier,
64
+ it.second->return_type.ToString().c_str(), value.type().ToString().c_str());
60
65
  }
61
- it.second->value = values[i];
66
+ it.second->SetValue(value);
62
67
  }
63
68
  }
64
69
 
65
- bool PreparedStatementData::TryGetType(idx_t param_idx, LogicalType &result) {
66
- auto it = value_map.find(param_idx);
70
+ bool PreparedStatementData::TryGetType(const string &identifier, LogicalType &result) {
71
+ auto it = value_map.find(identifier);
67
72
  if (it == value_map.end()) {
68
73
  return false;
69
74
  }
70
75
  if (it->second->return_type.id() != LogicalTypeId::INVALID) {
71
76
  result = it->second->return_type;
72
77
  } else {
73
- result = it->second->value.type();
78
+ result = it->second->GetValue().type();
74
79
  }
75
80
  return true;
76
81
  }
77
82
 
78
- LogicalType PreparedStatementData::GetType(idx_t param_idx) {
83
+ LogicalType PreparedStatementData::GetType(const string &identifier) {
79
84
  LogicalType result;
80
- if (!TryGetType(param_idx, result)) {
81
- throw BinderException("Could not find parameter with index %llu", param_idx);
85
+ if (!TryGetType(identifier, result)) {
86
+ throw BinderException("Could not find parameter identified with: %s", identifier);
82
87
  }
83
88
  return result;
84
89
  }
@@ -11,36 +11,36 @@
11
11
  namespace duckdb {
12
12
 
13
13
  ParameterExpression::ParameterExpression()
14
- : ParsedExpression(ExpressionType::VALUE_PARAMETER, ExpressionClass::PARAMETER), parameter_nr(0) {
14
+ : ParsedExpression(ExpressionType::VALUE_PARAMETER, ExpressionClass::PARAMETER) {
15
15
  }
16
16
 
17
17
  string ParameterExpression::ToString() const {
18
- return "$" + to_string(parameter_nr);
18
+ return "$" + identifier;
19
19
  }
20
20
 
21
21
  unique_ptr<ParsedExpression> ParameterExpression::Copy() const {
22
22
  auto copy = make_uniq<ParameterExpression>();
23
- copy->parameter_nr = parameter_nr;
23
+ copy->identifier = identifier;
24
24
  copy->CopyProperties(*this);
25
25
  return std::move(copy);
26
26
  }
27
27
 
28
28
  bool ParameterExpression::Equal(const ParameterExpression &a, const ParameterExpression &b) {
29
- return a.parameter_nr == b.parameter_nr;
29
+ return StringUtil::CIEquals(a.identifier, b.identifier);
30
30
  }
31
31
 
32
32
  hash_t ParameterExpression::Hash() const {
33
33
  hash_t result = ParsedExpression::Hash();
34
- return CombineHash(duckdb::Hash(parameter_nr), result);
34
+ return CombineHash(duckdb::Hash(identifier.c_str(), identifier.size()), result);
35
35
  }
36
36
 
37
37
  void ParameterExpression::Serialize(FieldWriter &writer) const {
38
- writer.WriteField<idx_t>(parameter_nr);
38
+ writer.WriteString(identifier);
39
39
  }
40
40
 
41
41
  unique_ptr<ParsedExpression> ParameterExpression::Deserialize(ExpressionType type, FieldReader &reader) {
42
42
  auto expression = make_uniq<ParameterExpression>();
43
- expression->parameter_nr = reader.ReadRequired<idx_t>();
43
+ expression->identifier = reader.ReadRequired<string>();
44
44
  return std::move(expression);
45
45
  }
46
46
 
@@ -6,8 +6,8 @@ ExecuteStatement::ExecuteStatement() : SQLStatement(StatementType::EXECUTE_STATE
6
6
  }
7
7
 
8
8
  ExecuteStatement::ExecuteStatement(const ExecuteStatement &other) : SQLStatement(other), name(other.name) {
9
- for (const auto &value : other.values) {
10
- values.push_back(value->Copy());
9
+ for (const auto &item : other.named_values) {
10
+ named_values.emplace(std::make_pair(item.first, item.second->Copy()));
11
11
  }
12
12
  }
13
13
 
@@ -27,10 +27,19 @@ unique_ptr<ParsedExpression> Transformer::TransformArrayAccess(duckdb_libpgquery
27
27
  children.push_back(std::move(result));
28
28
  if (index->is_slice) {
29
29
  // slice
30
- children.push_back(!index->lidx ? make_uniq<ConstantExpression>(Value())
31
- : TransformExpression(index->lidx));
32
- children.push_back(!index->uidx ? make_uniq<ConstantExpression>(Value())
33
- : TransformExpression(index->uidx));
30
+ // if either the lower or upper bound is not specified, we use an empty const list so that we can
31
+ // handle it in the execution
32
+ unique_ptr<ParsedExpression> lower =
33
+ index->lidx ? TransformExpression(index->lidx)
34
+ : make_uniq<ConstantExpression>(Value::LIST(LogicalType::INTEGER, vector<Value>()));
35
+ children.push_back(std::move(lower));
36
+ unique_ptr<ParsedExpression> upper =
37
+ index->uidx ? TransformExpression(index->uidx)
38
+ : make_uniq<ConstantExpression>(Value::LIST(LogicalType::INTEGER, vector<Value>()));
39
+ children.push_back(std::move(upper));
40
+ if (index->step) {
41
+ children.push_back(TransformExpression(index->step));
42
+ }
34
43
  result = make_uniq<OperatorExpression>(ExpressionType::ARRAY_SLICE, std::move(children));
35
44
  } else {
36
45
  // array access
@@ -4,40 +4,59 @@
4
4
 
5
5
  namespace duckdb {
6
6
 
7
- unique_ptr<ParsedExpression> Transformer::TransformParamRef(duckdb_libpgquery::PGParamRef &node) {
8
- auto expr = make_uniq<ParameterExpression>();
7
+ namespace {
8
+
9
+ struct PreparedParam {
10
+ PreparedParamType type;
11
+ string identifier;
12
+ };
13
+
14
+ } // namespace
15
+
16
+ static PreparedParam GetParameterIdentifier(duckdb_libpgquery::PGParamRef &node) {
17
+ PreparedParam param;
18
+ if (node.name) {
19
+ param.type = PreparedParamType::NAMED;
20
+ param.identifier = node.name;
21
+ return param;
22
+ }
9
23
  if (node.number < 0) {
10
24
  throw ParserException("Parameter numbers cannot be negative");
11
25
  }
26
+ param.identifier = StringUtil::Format("%d", node.number);
27
+ param.type = node.number == 0 ? PreparedParamType::AUTO_INCREMENT : PreparedParamType::POSITIONAL;
28
+ return param;
29
+ }
12
30
 
13
- if (node.name) {
14
- // This is a named parameter, try to find an entry for it
15
- D_ASSERT(node.number == 0);
16
- int32_t index;
17
- if (GetNamedParam(node.name, index)) {
18
- // We've seen this named parameter before and assigned it an index!
19
- node.number = index;
20
- }
21
- }
22
- if (node.number == 0) {
23
- expr->parameter_nr = ParamCount() + 1;
24
- if (node.name && !HasNamedParameters() && ParamCount() != 0) {
25
- // This parameter is named, but there were other parameter before it, and they were not named
26
- throw NotImplementedException("Mixing positional and named parameters is not supported yet");
31
+ unique_ptr<ParsedExpression> Transformer::TransformParamRef(duckdb_libpgquery::PGParamRef &node) {
32
+ auto expr = make_uniq<ParameterExpression>();
33
+
34
+ auto param = GetParameterIdentifier(node);
35
+ idx_t known_param_index = DConstants::INVALID_INDEX;
36
+ // This is a named parameter, try to find an entry for it
37
+ GetParam(param.identifier, known_param_index, param.type);
38
+
39
+ if (known_param_index == DConstants::INVALID_INDEX) {
40
+ // We have not seen this parameter before
41
+ if (node.number != 0) {
42
+ // Preserve the parameter number
43
+ known_param_index = node.number;
44
+ } else {
45
+ known_param_index = ParamCount() + 1;
46
+ if (!node.name) {
47
+ param.identifier = StringUtil::Format("%d", known_param_index);
48
+ }
27
49
  }
28
- if (node.name) {
29
- D_ASSERT(!named_param_map.count(node.name));
50
+
51
+ if (!named_param_map.count(param.identifier)) {
30
52
  // Add it to the named parameter map so we can find it next time it's referenced
31
- SetNamedParam(node.name, expr->parameter_nr);
53
+ SetParam(param.identifier, known_param_index, param.type);
32
54
  }
33
- } else {
34
- if (!node.name && HasNamedParameters()) {
35
- // This parameter does not have a name, but the named param map is not empty
36
- throw NotImplementedException("Mixing positional and named parameters is not supported yet");
37
- }
38
- expr->parameter_nr = node.number;
39
55
  }
40
- SetParamCount(MaxValue<idx_t>(ParamCount(), expr->parameter_nr));
56
+
57
+ expr->identifier = param.identifier;
58
+ idx_t new_param_count = MaxValue<idx_t>(ParamCount(), known_param_index);
59
+ SetParamCount(new_param_count);
41
60
  return std::move(expr);
42
61
  }
43
62
 
@@ -2,6 +2,8 @@
2
2
  #include "duckdb/parser/statement/execute_statement.hpp"
3
3
  #include "duckdb/parser/statement/prepare_statement.hpp"
4
4
  #include "duckdb/parser/transformer.hpp"
5
+ #include "duckdb/parser/expression/comparison_expression.hpp"
6
+ #include "duckdb/parser/expression/columnref_expression.hpp"
5
7
 
6
8
  namespace duckdb {
7
9
 
@@ -13,26 +15,46 @@ unique_ptr<PrepareStatement> Transformer::TransformPrepare(duckdb_libpgquery::PG
13
15
  auto result = make_uniq<PrepareStatement>();
14
16
  result->name = string(stmt.name);
15
17
  result->statement = TransformStatement(*stmt.query);
16
- if (!result->statement->named_param_map.empty()) {
17
- throw NotImplementedException("Named parameters are not supported in this client yet");
18
- }
19
18
  SetParamCount(0);
20
19
 
21
20
  return result;
22
21
  }
23
22
 
23
+ static string NotAcceptedExpressionException() {
24
+ return "Only scalar parameters, named parameters or NULL supported for EXECUTE";
25
+ }
26
+
24
27
  unique_ptr<ExecuteStatement> Transformer::TransformExecute(duckdb_libpgquery::PGExecuteStmt &stmt) {
25
28
  auto result = make_uniq<ExecuteStatement>();
26
29
  result->name = string(stmt.name);
27
30
 
31
+ vector<unique_ptr<ParsedExpression>> intermediate_values;
28
32
  if (stmt.params) {
29
- TransformExpressionList(*stmt.params, result->values);
33
+ TransformExpressionList(*stmt.params, intermediate_values);
30
34
  }
31
- for (auto &expr : result->values) {
35
+
36
+ idx_t param_idx = 0;
37
+ for (idx_t i = 0; i < intermediate_values.size(); i++) {
38
+ auto &expr = intermediate_values[i];
32
39
  if (!expr->IsScalar()) {
33
- throw Exception("Only scalar parameters or NULL supported for EXECUTE");
40
+ throw InvalidInputException(NotAcceptedExpressionException());
41
+ }
42
+ if (!expr->alias.empty() && param_idx != 0) {
43
+ // Found unnamed parameters mixed with named parameters
44
+ throw NotImplementedException("Mixing named parameters and positional parameters is not supported yet");
45
+ }
46
+ auto param_name = expr->alias;
47
+ if (expr->alias.empty()) {
48
+ param_name = std::to_string(param_idx + 1);
49
+ if (param_idx != i) {
50
+ throw NotImplementedException("Mixing named parameters and positional parameters is not supported yet");
51
+ }
52
+ param_idx++;
34
53
  }
54
+ expr->alias.clear();
55
+ result->named_values[param_name] = std::move(expr);
35
56
  }
57
+ intermediate_values.clear();
36
58
  return result;
37
59
  }
38
60
 
@@ -91,24 +91,42 @@ void Transformer::SetParamCount(idx_t new_count) {
91
91
  auto &root = RootTransformer();
92
92
  root.prepared_statement_parameter_index = new_count;
93
93
  }
94
- void Transformer::SetNamedParam(const string &name, int32_t index) {
94
+
95
+ static void ParamTypeCheck(PreparedParamType last_type, PreparedParamType new_type) {
96
+ // Mixing positional/auto-increment and named parameters is not supported
97
+ if (last_type == PreparedParamType::INVALID) {
98
+ return;
99
+ }
100
+ if (last_type == PreparedParamType::NAMED) {
101
+ if (new_type != PreparedParamType::NAMED) {
102
+ throw NotImplementedException("Mixing named and positional parameters is not supported yet");
103
+ }
104
+ }
105
+ if (last_type != PreparedParamType::NAMED) {
106
+ if (new_type == PreparedParamType::NAMED) {
107
+ throw NotImplementedException("Mixing named and positional parameters is not supported yet");
108
+ }
109
+ }
110
+ }
111
+
112
+ void Transformer::SetParam(const string &identifier, idx_t index, PreparedParamType type) {
95
113
  auto &root = RootTransformer();
96
- D_ASSERT(!root.named_param_map.count(name));
97
- root.named_param_map[name] = index;
114
+ ParamTypeCheck(root.last_param_type, type);
115
+ root.last_param_type = type;
116
+ D_ASSERT(!root.named_param_map.count(identifier));
117
+ root.named_param_map[identifier] = index;
98
118
  }
99
- bool Transformer::GetNamedParam(const string &name, int32_t &index) {
119
+
120
+ bool Transformer::GetParam(const string &identifier, idx_t &index, PreparedParamType type) {
100
121
  auto &root = RootTransformer();
101
- auto entry = root.named_param_map.find(name);
122
+ ParamTypeCheck(root.last_param_type, type);
123
+ auto entry = root.named_param_map.find(identifier);
102
124
  if (entry == root.named_param_map.end()) {
103
125
  return false;
104
126
  }
105
127
  index = entry->second;
106
128
  return true;
107
129
  }
108
- bool Transformer::HasNamedParameters() const {
109
- auto &root = RootTransformer();
110
- return !root.named_param_map.empty();
111
- }
112
130
 
113
131
  unique_ptr<SQLStatement> Transformer::TransformStatementInternal(duckdb_libpgquery::PGNode &stmt) {
114
132
  switch (stmt.type) {