backend/query/query_validator.h (116 lines of code) (raw):

// // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef THIRD_PARTY_CLOUD_SPANNER_EMULATOR_BACKEND_QUERY_QUERY_VALIDATOR_H_ #define THIRD_PARTY_CLOUD_SPANNER_EMULATOR_BACKEND_QUERY_QUERY_VALIDATOR_H_ #include <stdbool.h> #include <utility> #include "zetasql/public/language_options.h" #include "zetasql/resolved_ast/resolved_ast.h" #include "zetasql/resolved_ast/resolved_ast_visitor.h" #include "zetasql/resolved_ast/resolved_node.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "backend/query/analyzer_options.h" #include "backend/query/feature_filter/sql_features_view.h" #include "backend/query/query_context.h" #include "backend/query/query_engine_options.h" #include "backend/schema/catalog/index.h" #include "backend/schema/catalog/schema.h" namespace google { namespace spanner { namespace emulator { namespace backend { bool IsSearchQueryAllowed(const QueryEngineOptions* options, const QueryContext& context); // Returns true if the given node or any of its descendants is a // ResolvedLockMode node which makes this a query that uses SELECT FOR UPDATE. bool IsSelectForUpdateQuery(const zetasql::ResolvedNode& node); // QueryFeatures provides information about features used in a query. struct QueryFeatures { // If true, we're in a query with a FOR UPDATE clause. Required for returning // appropriate errors when combined with other features. bool has_for_update = false; // If true, it affects errors returned by FOR UPDATE queries. The value of // the hint is ignored even if it was specified since the emulator doesn't // acquire locks. bool has_lock_scanned_ranges = false; }; // Implements ResolvedASTVisitor to validate various nodes in an AST and // collect information of interest (such as index names). class QueryValidator : public zetasql::ResolvedASTVisitor { public: explicit QueryValidator(const QueryContext context, QueryEngineOptions* extracted_options = nullptr, const zetasql::LanguageOptions language_options = MakeGoogleSqlLanguageOptions()) : context_(std::move(context)), language_options_(std::move(language_options)), sql_features_(SqlFeaturesView()), extracted_options_(extracted_options) {} // Returns the list of indexes used by the query being validated. Must // be called after the entire tree has been visited. const absl::flat_hash_set<const Index*>& indexes_used() const { return indexes_used_; } void set_in_partition_query(bool in_partition_query) { in_partition_query_ = in_partition_query; } const absl::flat_hash_set<const SchemaNode*>& dependent_sequences() { return dependent_sequences_; } protected: absl::Status DefaultVisit(const zetasql::ResolvedNode* node) override; absl::Status VisitResolvedQueryStmt( const zetasql::ResolvedQueryStmt* node) override; absl::Status VisitResolvedLiteral( const zetasql::ResolvedLiteral* node) override; absl::Status VisitResolvedFunctionCall( const zetasql::ResolvedFunctionCall* node) override; absl::Status VisitResolvedAggregateFunctionCall( const zetasql::ResolvedAggregateFunctionCall* node) override; absl::Status VisitResolvedCast(const zetasql::ResolvedCast* node) override; absl::Status VisitResolvedSampleScan( const zetasql::ResolvedSampleScan* node) override; absl::Status VisitResolvedInsertStmt( const zetasql::ResolvedInsertStmt* node) override; absl::Status VisitResolvedUpdateStmt( const zetasql::ResolvedUpdateStmt* node) override; absl::Status VisitResolvedDeleteStmt( const zetasql::ResolvedDeleteStmt* node) override; absl::Status VisitResolvedTableScan( const zetasql::ResolvedTableScan* node) override; const Schema* schema() const { return context_.schema; } bool IsSequenceFunction(const zetasql::ResolvedFunctionCall* node) const; absl::Status ValidateSequenceFunction( const zetasql::ResolvedFunctionCall* node); private: // Validates the child hint nodes of `node`. absl::Status ValidateHints(const zetasql::ResolvedNode* node); // Returns an OK if `name` is a supported hint name for nodes with kind // `node_kind`; otherwise, returns an invalid argument error. absl::Status CheckSpannerHintName( absl::string_view name, const zetasql::ResolvedNodeKind node_kind) const; // Returns an OK if `name` is a supported Emulator-only hint name for nodes // with kind `node_kind`; otherwise, returns an invalid argument error. absl::Status CheckEmulatorHintName( absl::string_view name, const zetasql::ResolvedNodeKind node_kind) const; // Returns an OK if `value` represents a valid value for hints with name // `name` specified on a node of kind `node_kind`; otherwise, returns an // invalid argument error. `hint_map` is initialized to a mapping of // hint_name->hint_value of all the hints specified on the node. absl::Status CheckHintValue( absl::string_view name, const zetasql::Value& value, const zetasql::ResolvedNodeKind node_kind, const absl::flat_hash_map<absl::string_view, zetasql::Value>& hint_map); absl::Status CheckSearchFunctionsAreAllowed( const zetasql::ResolvedFunctionCall& function_call); // Validate that the presence of a ResolvedLockMode node (FOR UPDATE in a // SELECT query) is valid in this context. absl::Status CheckTableScanLockModeAllowed(const QueryEngineOptions* options, const QueryContext& context) const; // Check a node's hint map and extracts query engine options from any Spanner // hints absl::Status ExtractSpannerOptionsForNode( const absl::flat_hash_map<absl::string_view, zetasql::Value>& node_hint_map); // Extracts query engine options from any Emulator-specific hints // 'node_hint_map' for a node. absl::Status ExtractEmulatorOptionsForNode( const absl::flat_hash_map<absl::string_view, zetasql::Value>& node_hint_map) const; // Enforces restrictions on reading tables/columns containing pending commit // timestamps. If `access_list` is non-empty it will be used to ignore columns // which are not read. absl::Status CheckPendingCommitTimestampReads( const zetasql::ResolvedTableScan* table_scan, absl::Span<const zetasql::ResolvedStatement::ObjectAccess> access_list = {}); // Returns true if the function is allowed in read-write transactions // only. E.g. GET_NEXT_SEQUENCE_VALUE(). bool IsReadWriteOnlyFunction(absl::string_view name) const; const QueryContext context_; const zetasql::LanguageOptions language_options_; bool in_partition_query_ = false; const SqlFeaturesView sql_features_; // List of indexes used by the query being validated. absl::flat_hash_set<const Index*> indexes_used_; // List of sequences used by the query being validated. absl::flat_hash_set<const SchemaNode*> dependent_sequences_; // Table scans from DML statements. Depending on context, the column list // on these scans may include columns which are written, but not read. This // becomes relevant when we enforce pending commit timestamp restrictions. absl::flat_hash_set<const zetasql::ResolvedTableScan*> dml_table_scans_; // Options for the query engine that are extracted through user-specified // hints. QueryEngineOptions* extracted_options_; // Features used by the query. Required to verify if a feature used in one // part of a query can be combined with features used in another part of the // query. QueryFeatures query_features_; }; } // namespace backend } // namespace emulator } // namespace spanner } // namespace google #endif // THIRD_PARTY_CLOUD_SPANNER_EMULATOR_BACKEND_QUERY_QUERY_VALIDATOR_H_