Skip to main content

frontend/instance/
copy_in.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use api::v1::RowInsertRequests;
16use async_trait::async_trait;
17use auth::{PermissionChecker, PermissionCheckerRef, PermissionReq};
18use common_error::ext::BoxedError;
19use common_query::Output;
20use query::query_engine::options::{QueryOptions, validate_catalog_and_schema};
21use servers::error::{AuthSnafu, ExecuteGrpcRequestSnafu, ExecuteQuerySnafu, Result};
22use servers::query_handler::CopyInHandler;
23use session::context::QueryContextRef;
24use snafu::ResultExt;
25use table::metadata::TableInfoRef;
26
27use crate::instance::Instance;
28
29#[async_trait]
30impl CopyInHandler for Instance {
31    async fn copy_in_table(
32        &self,
33        catalog: &str,
34        schema: &str,
35        table: &str,
36        query_ctx: QueryContextRef,
37    ) -> Result<Option<TableInfoRef>> {
38        // Mirror SQL INSERT's catalog restriction (see `check_permission`):
39        // when cross-catalog queries are disallowed, a COPY may only
40        // target the current catalog. This must run against the ORIGINAL
41        // query context — `begin_copy_in` switches the context to the
42        // target catalog afterwards, so nothing downstream would catch
43        // it.
44        let disallow_cross_catalog_query = self
45            .plugins
46            .get::<QueryOptions>()
47            .map(|opts| opts.disallow_cross_catalog_query)
48            .unwrap_or_default();
49        if disallow_cross_catalog_query {
50            validate_catalog_and_schema(catalog, schema, &query_ctx)
51                .map_err(BoxedError::new)
52                .context(ExecuteQuerySnafu)?;
53        }
54
55        // A COPY writes rows like a SQL INSERT; check the write permission
56        // before the client starts streaming data.
57        self.plugins
58            .get::<PermissionCheckerRef>()
59            .as_ref()
60            .check_permission(
61                query_ctx.current_user(),
62                PermissionReq::BulkInsert {
63                    catalog,
64                    schema,
65                    table,
66                },
67            )
68            .context(AuthSnafu)?;
69
70        self.catalog_manager()
71            .table(catalog, schema, table, None)
72            .await
73            .map(|table| table.map(|table| table.table_info()))
74            .map_err(servers::error::Error::from)
75    }
76
77    async fn copy_in_insert(
78        &self,
79        requests: RowInsertRequests,
80        query_ctx: QueryContextRef,
81    ) -> Result<Output> {
82        self.handle_row_inserts(requests, query_ctx, false, false)
83            .await
84            .map_err(BoxedError::new)
85            .context(ExecuteGrpcRequestSnafu)
86    }
87}