backend/query/pg_catalog.cc (1,748 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.
//
#include "backend/query/pg_catalog.h"
#include <map>
#include <string>
#include <vector>
#include "zetasql/public/catalog.h"
#include "zetasql/public/simple_catalog.h"
#include "zetasql/public/types/type_factory.h"
#include "zetasql/public/value.h"
#include "zetasql/base/no_destructor.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "backend/query/info_schema_columns_metadata_values.h"
#include "backend/query/tables_from_metadata.h"
#include "backend/schema/catalog/change_stream.h"
#include "backend/schema/catalog/check_constraint.h"
#include "backend/schema/catalog/column.h"
#include "backend/schema/catalog/foreign_key.h"
#include "backend/schema/catalog/index.h"
#include "backend/schema/catalog/named_schema.h"
#include "backend/schema/catalog/schema.h"
#include "backend/schema/catalog/sequence.h"
#include "backend/schema/catalog/table.h"
#include "backend/schema/catalog/view.h"
#include "third_party/spanner_pg/bootstrap_catalog/bootstrap_catalog.h"
#include "third_party/spanner_pg/catalog/type.h"
#include "third_party/spanner_pg/datatypes/extended/pg_oid_type.h"
#include "third_party/spanner_pg/interface/bootstrap_catalog_accessor.h"
namespace postgres_translator {
namespace {
static constexpr char kDefaultSchema[] = "public";
static constexpr char kPGAm[] = "pg_am";
static constexpr char kPGAttrdef[] = "pg_attrdef";
static constexpr char kPGAttribute[] = "pg_attribute";
static constexpr char kPGAvailableExtensionVersions[] =
"pg_available_extension_versions";
static constexpr char kPGAvailableExtensions[] = "pg_available_extensions";
static constexpr char kPGBackendMemoryContexts[] = "pg_backend_memory_contexts";
static constexpr char kPGClass[] = "pg_class";
static constexpr char kPGCollation[] = "pg_collation";
static constexpr char kPGConfig[] = "pg_config";
static constexpr char kPGConstraint[] = "pg_constraint";
static constexpr char kPGCursors[] = "pg_cursors";
static constexpr char kPGDescription[] = "pg_description";
static constexpr char kPGEnum[] = "pg_enum";
static constexpr char kPGExtension[] = "pg_extension";
static constexpr char kPGFileSettings[] = "pg_file_settings";
static constexpr char kPGHbaFileRules[] = "pg_hba_file_rules";
static constexpr char kPGIndex[] = "pg_index";
static constexpr char kPGIndexes[] = "pg_indexes";
static constexpr char kPGLanguage[] = "pg_language";
static constexpr char kPGMatviews[] = "pg_matviews";
static constexpr char kPGNamespace[] = "pg_namespace";
static constexpr char kPGPolicies[] = "pg_policies";
static constexpr char kPGPreparedXacts[] = "pg_prepared_xacts";
static constexpr char kPGProc[] = "pg_proc";
static constexpr char kPGPublicationTables[] = "pg_publication_tables";
static constexpr char kPGRange[] = "pg_range";
static constexpr char kPGRoles[] = "pg_roles";
static constexpr char kPGRules[] = "pg_rules";
static constexpr char kPGSequence[] = "pg_sequence";
static constexpr char kPGSequences[] = "pg_sequences";
static constexpr char kPGSettings[] = "pg_settings";
static constexpr char kPGShmemAllocations[] = "pg_shmem_allocations";
static constexpr char kPGTables[] = "pg_tables";
static constexpr char kPGType[] = "pg_type";
static constexpr char kPGViews[] = "pg_views";
using google::spanner::emulator::backend::ChangeStream;
using google::spanner::emulator::backend::CheckConstraint;
using google::spanner::emulator::backend::Column;
using google::spanner::emulator::backend::ForeignKey;
using google::spanner::emulator::backend::Index;
using google::spanner::emulator::backend::KeyColumn;
using google::spanner::emulator::backend::kSpannerPGTypeToGSQLType;
using google::spanner::emulator::backend::NamedSchema;
using google::spanner::emulator::backend::PGCatalogColumnsMetadata;
using google::spanner::emulator::backend::PGColumnsMetadata;
using google::spanner::emulator::backend::Schema;
using google::spanner::emulator::backend::SDLObjectName;
using google::spanner::emulator::backend::Sequence;
using google::spanner::emulator::backend::SpannerSysColumnsMetadata;
using google::spanner::emulator::backend::Table;
using google::spanner::emulator::backend::View;
using ::zetasql::types::Int64ArrayType;
using ::zetasql::types::StringArrayType;
using ::zetasql::values::Bool;
using ::zetasql::values::Int64;
using ::zetasql::values::Int64Array;
using ::zetasql::values::Null;
using ::zetasql::values::NullBool;
using ::zetasql::values::NullDouble;
using ::zetasql::values::NullInt64;
using ::zetasql::values::NullString;
using ::zetasql::values::String;
using ::zetasql::values::StringArray;
using postgres_translator::spangres::datatypes::CreatePgOidValue;
using postgres_translator::spangres::datatypes::GetPgOidArrayType;
using spangres::datatypes::NullPgOid;
struct PgClassSystemTableMetadata {
std::string table_name;
int table_oid;
int namespace_oid;
int column_count;
bool is_view;
};
using spangres::datatypes::GetPgOidArrayType;
static const zetasql_base::NoDestructor<absl::flat_hash_set<std::string>>
kSupportedTables{{
kPGAm,
kPGAttrdef,
kPGAttribute,
kPGAvailableExtensionVersions,
kPGAvailableExtensions,
kPGBackendMemoryContexts,
kPGClass,
kPGCollation,
kPGConfig,
kPGConstraint,
kPGCursors,
kPGDescription,
kPGEnum,
kPGExtension,
kPGFileSettings,
kPGHbaFileRules,
kPGIndex,
kPGIndexes,
kPGLanguage,
kPGMatviews,
kPGNamespace,
kPGPolicies,
kPGPreparedXacts,
kPGProc,
kPGPublicationTables,
kPGRange,
kPGRoles,
kPGRules,
kPGSequence,
kPGSequences,
kPGSettings,
kPGShmemAllocations,
kPGTables,
kPGType,
kPGViews,
}};
const auto kSupportedCollations =
absl::flat_hash_set<absl::string_view>({"default", "C"});
const auto kHardCodedNamedSchemaOid =
absl::flat_hash_map<absl::string_view, uint32_t>(
{{"pg_catalog", 11},
{"public", 2200},
{"spanner", 50000},
{"information_schema", 75003},
{"spanner_sys", 75004}});
// System table OID mappings.
const auto kHardCodedPgCatalogTableOid =
absl::flat_hash_map<absl::string_view, uint32_t>({
{"pg_catalog.pg_aggregate", 2600},
{"pg_catalog.pg_am", 2601},
{"pg_catalog.pg_amop", 2602},
{"pg_catalog.pg_amproc", 2603},
{"pg_catalog.pg_attrdef", 2604},
{"pg_catalog.pg_attribute", 1249},
{"pg_catalog.pg_auth_members", 1261},
{"pg_catalog.pg_authid", 1260},
{"pg_catalog.pg_cast", 2605},
{"pg_catalog.pg_class", 1259},
{"pg_catalog.pg_collation", 3456},
{"pg_catalog.pg_constraint", 2606},
{"pg_catalog.pg_conversion", 2607},
{"pg_catalog.pg_database", 1262},
{"pg_catalog.pg_db_role_setting", 2964},
{"pg_catalog.pg_default_acl", 826},
{"pg_catalog.pg_depend", 2608},
{"pg_catalog.pg_description", 2609},
{"pg_catalog.pg_enum", 3501},
{"pg_catalog.pg_event_trigger", 3466},
{"pg_catalog.pg_extension", 3079},
{"pg_catalog.pg_foreign_data_wrapper", 2328},
{"pg_catalog.pg_foreign_server", 1417},
{"pg_catalog.pg_foreign_table", 3118},
{"pg_catalog.pg_index", 2610},
{"pg_catalog.pg_inherits", 2611},
{"pg_catalog.pg_init_privs", 3394},
{"pg_catalog.pg_language", 2612},
{"pg_catalog.pg_largeobject", 2613},
{"pg_catalog.pg_largeobject_metadata", 2995},
{"pg_catalog.pg_namespace", 2615},
{"pg_catalog.pg_opclass", 2616},
{"pg_catalog.pg_operator", 2617},
{"pg_catalog.pg_opfamily", 2753},
{"pg_catalog.pg_parameter_acl", 6243},
{"pg_catalog.pg_partitioned_table", 3350},
{"pg_catalog.pg_policy", 3256},
{"pg_catalog.pg_proc", 1255},
{"pg_catalog.pg_publication", 6104},
{"pg_catalog.pg_publication_namespace", 6237},
{"pg_catalog.pg_publication_rel", 6106},
{"pg_catalog.pg_range", 3541},
{"pg_catalog.pg_replication_origin", 6000},
{"pg_catalog.pg_rewrite", 2618},
{"pg_catalog.pg_seclabel", 3596},
{"pg_catalog.pg_sequence", 2224},
{"pg_catalog.pg_shdepend", 1214},
{"pg_catalog.pg_shdescription", 2396},
{"pg_catalog.pg_shseclabel", 3592},
{"pg_catalog.pg_statistic", 2619},
{"pg_catalog.pg_statistic_ext", 3381},
{"pg_catalog.pg_statistic_ext_data", 3429},
{"pg_catalog.pg_subscription", 6100},
{"pg_catalog.pg_subscription_rel", 6102},
{"pg_catalog.pg_tablespace", 1213},
{"pg_catalog.pg_transform", 3576},
{"pg_catalog.pg_trigger", 2620},
{"pg_catalog.pg_ts_config", 3602},
{"pg_catalog.pg_ts_config_map", 3603},
{"pg_catalog.pg_ts_dict", 3600},
{"pg_catalog.pg_ts_parser", 3601},
{"pg_catalog.pg_ts_template", 3764},
{"pg_catalog.pg_type", 1247},
{"pg_catalog.pg_user_mapping", 1418},
});
// System view OID mappings.
const auto kHardCodedSystemViewOid =
absl::flat_hash_map<absl::string_view, uint32_t>({
{"pg_catalog.pg_available_extensions", 75008},
{"pg_catalog.pg_available_extension_versions", 75009},
{"pg_catalog.pg_backend_memory_contexts", 75010},
{"pg_catalog.pg_config", 75011},
{"pg_catalog.pg_cursors", 75012},
{"pg_catalog.pg_file_settings", 75013},
{"pg_catalog.pg_group", 75014},
{"pg_catalog.pg_hba_file_rules", 75015},
{"pg_catalog.pg_indexes", 75016},
{"pg_catalog.pg_locks", 75017},
{"pg_catalog.pg_matviews", 75018},
{"pg_catalog.pg_policies", 75019},
{"pg_catalog.pg_prepared_statements", 75020},
{"pg_catalog.pg_prepared_xacts", 75021},
{"pg_catalog.pg_publication_tables", 75022},
{"pg_catalog.pg_replication_origin_status", 75023},
{"pg_catalog.pg_replication_slots", 75024},
{"pg_catalog.pg_roles", 75025},
{"pg_catalog.pg_rules", 75026},
{"pg_catalog.pg_seclabels", 75027},
{"pg_catalog.pg_sequences", 75028},
{"pg_catalog.pg_shadow", 75029},
{"pg_catalog.pg_shmem_allocations", 75030},
{"pg_catalog.pg_stats", 75031},
{"pg_catalog.pg_stats_ext", 75032},
{"pg_catalog.pg_stats_ext_exprs", 75033},
{"pg_catalog.pg_tables", 75034},
{"pg_catalog.pg_timezone_abbrevs", 75035},
{"pg_catalog.pg_timezone_names", 75036},
{"pg_catalog.pg_user", 75037},
{"pg_catalog.pg_user_mappings", 75038},
{"pg_catalog.pg_views", 75039},
{"pg_catalog.pg_settings", 75040},
{"information_schema.information_schema_catalog_name", 75041},
{"information_schema.check_constraints", 75042},
{"information_schema.columns", 75043},
{"information_schema.column_column_usage", 75044},
{"information_schema.column_options", 75045},
{"information_schema.constraint_column_usage", 75046},
{"information_schema.constraint_table_usage", 75047},
{"information_schema.database_options", 75048},
{"information_schema.indexes", 75049},
{"information_schema.index_columns", 75050},
{"information_schema.key_column_usage", 75051},
{"information_schema.referential_constraints", 75052},
{"information_schema.schemata", 75053},
{"information_schema.spanner_statistics", 75054},
{"information_schema.table_constraints", 75055},
{"information_schema.tables", 75056},
{"information_schema.views", 75057},
{"information_schema.change_streams", 75058},
{"information_schema.change_stream_columns", 75059},
{"information_schema.change_stream_tables", 75060},
{"information_schema.change_stream_options", 75061},
{"information_schema.sequences", 75062},
{"information_schema.parameters", 75063},
{"information_schema.routines", 75064},
{"information_schema.routine_options", 75065},
{"information_schema.applicable_roles", 75066},
{"information_schema.change_stream_privileges", 75067},
{"information_schema.column_privileges", 75068},
{"information_schema.enabled_roles", 75069},
{"information_schema.routine_privileges", 75070},
{"information_schema.table_privileges", 75071},
{"information_schema.role_change_stream_grants", 75072},
{"information_schema.role_column_grants", 75073},
{"information_schema.role_routine_grants", 75074},
{"information_schema.role_table_grants", 75075},
{"information_schema.placements", 75113},
{"information_schema.placement_options", 75114},
{"spanner_sys.active_partitioned_dmls", 75077},
{"spanner_sys.active_queries_summary", 75078},
{"spanner_sys.lock_stats_top_10minute", 75079},
{"spanner_sys.lock_stats_top_hour", 75080},
{"spanner_sys.lock_stats_top_minute", 75081},
{"spanner_sys.lock_stats_total_10minute", 75082},
{"spanner_sys.lock_stats_total_hour", 75083},
{"spanner_sys.lock_stats_total_minute", 75084},
{"spanner_sys.oldest_active_queries", 75085},
{"spanner_sys.query_profiles_top_10minute", 75086},
{"spanner_sys.query_profiles_top_hour", 75087},
{"spanner_sys.query_profiles_top_minute", 75088},
{"spanner_sys.query_stats_top_10minute", 75089},
{"spanner_sys.query_stats_top_hour", 75090},
{"spanner_sys.query_stats_top_minute", 75091},
{"spanner_sys.query_stats_total_10minute", 75092},
{"spanner_sys.query_stats_total_hour", 75093},
{"spanner_sys.query_stats_total_minute", 75094},
{"spanner_sys.read_stats_top_10minute", 75095},
{"spanner_sys.read_stats_top_hour", 75096},
{"spanner_sys.read_stats_top_minute", 75097},
{"spanner_sys.read_stats_total_10minute", 75098},
{"spanner_sys.read_stats_total_hour", 75099},
{"spanner_sys.read_stats_total_minute", 75100},
{"spanner_sys.row_deletion_policies", 75101},
{"spanner_sys.supported_optimizer_versions", 75102},
{"spanner_sys.table_operations_stats_10minute", 75103},
{"spanner_sys.table_operations_stats_hour", 75104},
{"spanner_sys.table_operations_stats_minute", 75105},
{"spanner_sys.table_sizes_stats_1hour", 75106},
{"spanner_sys.txn_stats_top_10minute", 75107},
{"spanner_sys.txn_stats_top_hour", 75108},
{"spanner_sys.txn_stats_top_minute", 75109},
{"spanner_sys.txn_stats_total_10minute", 75110},
{"spanner_sys.txn_stats_total_hour", 75111},
{"spanner_sys.txn_stats_total_minute", 75112},
});
inline std::pair<std::string, std::string>
GetSchemaAndNameForPGCatalog(std::string table_name) {
const auto& [schema_part, name_part] =
SDLObjectName::SplitSchemaName(table_name);
return std::make_pair((schema_part.empty()) ? kDefaultSchema :
std::string(schema_part), std::string(name_part));
}
template <typename T>
std::string PrimaryKeyName(const T* table) {
std::string unqualified_table_name =
GetSchemaAndNameForPGCatalog(table->Name()).second;
return absl::StrCat("PK_", unqualified_table_name);
}
} // namespace
PGCatalog::PGCatalog(const EnumerableCatalog* root_catalog,
const Schema* default_schema)
: zetasql::SimpleCatalog(kName),
root_catalog_(root_catalog),
default_schema_(default_schema) {
tables_by_name_ = AddTablesFromMetadata(
PGCatalogColumnsMetadata(), *kSpannerPGTypeToGSQLType, *kSupportedTables);
for (auto& [name, table] : tables_by_name_) {
AddTable(table.get());
}
// Initialize metadata required for building tables.
for (const auto& column : PGCatalogColumnsMetadata()) {
pg_catalog_table_name_to_column_metadata_[column.table_name].push_back(
column);
}
for (const auto& column : PGColumnsMetadata()) {
info_schema_table_name_to_column_metadata_[column.table_name].push_back(
column);
}
for (const auto& column : SpannerSysColumnsMetadata()) {
spanner_sys_table_name_to_column_metadata_[column.table_name].push_back(
column);
}
FillPGAmTable();
FillPGAttrdefTable();
FillPGAttributeTable();
FillPGClassTable();
FillPGCollationTable();
FillPGConstraintTable();
FillPGIndexTable();
FillPGIndexesTable();
FillPGNamespaceTable();
FillPGProcTable();
FillPGSequenceTable();
FillPGSequencesTable();
FillPGSettingsTable();
FillPGTablesTable();
FillPGTypeTable();
FillPGViewsTable();
}
void PGCatalog::FillPGAmTable() {
auto pg_am = tables_by_name_.at(kPGAm).get();
// Mapping for access methods.
std::map<int, std::string> am_mappings = {
{75001, "t"},
{75002, "i"},
};
std::vector<std::vector<zetasql::Value>> rows;
rows.reserve(am_mappings.size());
for (const auto& [oid, amtype] : am_mappings) {
rows.push_back({
// oid
CreatePgOidValue(oid).value(),
// amname
String("spanner_default"),
// amtype
String(amtype),
});
}
pg_am->SetContents(rows);
}
void PGCatalog::FillPGAttrdefTable() {
auto pg_attrdef = tables_by_name_.at(kPGAttrdef).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const Table* table : default_schema_->tables()) {
if (!table->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Table " << table->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
int ordinal_position = 0;
for (const Column* column : table->columns()) {
++ordinal_position;
if (!column->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Column " << column->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
if (!column->is_identity_column() &&
(column->has_default_value() || column->is_generated())) {
if (!column->original_expression().has_value()) { continue; }
rows.push_back({
// oid
CreatePgOidValue(column->postgresql_oid().value()).value(),
// adrelid
CreatePgOidValue(table->postgresql_oid().value()).value(),
// adnum
Int64(ordinal_position),
// adbin
String(column->original_expression().value()),
});
}
}
}
pg_attrdef->SetContents(rows);
}
void PGCatalog::FillPGAttributeTable() {
auto pg_attribute = tables_by_name_.at(kPGAttribute).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const Table* table : default_schema_->tables()) {
if (!table->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Table " << table->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
if (!table->primary_key_index_postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "PK for " << table->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
// Add columns.
int ordinal_position = 1;
for (const Column* column : table->columns()) {
const PostgresTypeMapping* pg_type =
system_catalog_->GetTypeFromReverseMapping(column->GetType());
auto type = pg_type->PostgresTypeOid();
type = type == TEXTOID ? VARCHAROID : type;
type = type == TEXTARRAYOID ? VARCHARARRAYOID : type;
rows.push_back({
// attrelid
CreatePgOidValue(table->postgresql_oid().value()).value(),
// attname
String(column->Name()),
// atttypid
CreatePgOidValue(type).value(),
// attstattarget
NullInt64(),
// attlen
NullInt64(),
// attnum
Int64(ordinal_position),
// attndims
Int64(column->GetType()->IsArray() ? 1 : 0),
// attcacheoff
Int64(-1),
// atttypmod
NullInt64(),
// attbyval
NullBool(),
// attalign
NullString(),
// attstorage
NullString(),
// attcompression
String(std::string{'\0'}),
// attnotnull
Bool(!column->is_nullable()),
// atthasdef
Bool(column->has_default_value()),
// atthasmissing
Bool(false),
// attidentity
String(column->is_identity_column() ? "d" : std::string{'\0'}),
// attgenerated
String(column->is_generated() ? "s" : std::string{'\0'}),
// attisdropped
Bool(false),
// attislocal
Bool(true),
// attinhcount
Int64(0),
// attcollation
NullPgOid(),
// attoptions
NullString(),
// attfdwoptions
NullString(),
});
++ordinal_position;
}
// Add primary key columns.
ordinal_position = 1;
for (const KeyColumn* key_column : table->primary_key()) {
const PostgresTypeMapping* pg_type =
system_catalog_->GetTypeFromReverseMapping(
key_column->column()->GetType());
auto type = pg_type->PostgresTypeOid();
type = type == TEXTOID ? VARCHAROID : type;
type = type == TEXTARRAYOID ? VARCHARARRAYOID : type;
rows.push_back({
// attrelid
CreatePgOidValue(table->primary_key_index_postgresql_oid().value())
.value(),
// attname
String(key_column->column()->Name()),
// atttypid
CreatePgOidValue(type).value(),
// attstattarget
NullInt64(),
// attlen
NullInt64(),
// attnum
Int64(ordinal_position++),
// attndims
Int64(key_column->column()->GetType()->IsArray() ? 1 : 0),
// attcacheoff
Int64(-1),
// atttypmod
NullInt64(),
// attbyval
NullBool(),
// attalign
NullString(),
// attstorage
NullString(),
// attcompression
String(std::string{'\0'}),
// attnotnull
Bool(!key_column->column()->is_nullable()),
// atthasdef
Bool(key_column->column()->has_default_value()),
// atthasmissing
Bool(false),
// attidentity
String(std::string{'\0'}),
// attgenerated
String(key_column->column()->is_generated() ? "s"
: std::string{'\0'}),
// attisdropped
Bool(false),
// attislocal
Bool(true),
// attinhcount
Int64(0),
// attcollation
NullPgOid(),
// attoptions
NullString(),
// attfdwoptions
NullString(),
});
}
for (const Index* index : table->indexes()) {
if (!index->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Index " << index->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
int index_ordinal_position = 1;
for (const KeyColumn* key_column : index->key_columns()) {
const PostgresTypeMapping* pg_type =
system_catalog_->GetTypeFromReverseMapping(
key_column->column()->GetType());
auto type = pg_type->PostgresTypeOid();
type = type == TEXTOID ? VARCHAROID : type;
type = type == TEXTARRAYOID ? VARCHARARRAYOID : type;
rows.push_back({
// attrelid
CreatePgOidValue(index->postgresql_oid().value()).value(),
// attname
String(key_column->column()->Name()),
// atttypid
CreatePgOidValue(type).value(),
// attstattarget
NullInt64(),
// attlen
NullInt64(),
// attnum
Int64(index_ordinal_position++),
// attndims
Int64(key_column->column()->GetType()->IsArray() ? 1 : 0),
// attcacheoff
Int64(-1),
// atttypmod
NullInt64(),
// attbyval
NullBool(),
// attalign
NullString(),
// attstorage
NullString(),
// attcompression
String(std::string{'\0'}),
// attnotnull
Bool(!key_column->column()->is_nullable()),
// atthasdef
Bool(key_column->column()->has_default_value()),
// atthasmissing
Bool(false),
// attidentity
String(std::string{'\0'}),
// attgenerated
String(key_column->column()->is_generated() ? "s"
: std::string{'\0'}),
// attisdropped
Bool(false),
// attislocal
Bool(true),
// attinhcount
Int64(0),
// attcollation
NullPgOid(),
// attoptions
NullString(),
// attfdwoptions
NullString(),
});
}
index_ordinal_position++; // Account for inheritted primary key.
for (const Column* column : index->stored_columns()) {
const PostgresTypeMapping* pg_type =
system_catalog_->GetTypeFromReverseMapping(column->GetType());
auto type = pg_type->PostgresTypeOid();
type = type == TEXTOID ? VARCHAROID : type;
type = type == TEXTARRAYOID ? VARCHARARRAYOID : type;
rows.push_back({
// attrelid
CreatePgOidValue(index->postgresql_oid().value()).value(),
// attname
String(column->Name()),
// atttypid
CreatePgOidValue(type).value(),
// attstattarget
NullInt64(),
// attlen
NullInt64(),
// attnum
Int64(index_ordinal_position++),
// attndims
Int64(column->GetType()->IsArray() ? 1 : 0),
// attcacheoff
Int64(-1),
// atttypmod
NullInt64(),
// attbyval
NullBool(),
// attalign
NullString(),
// attstorage
NullString(),
// attcompression
String(std::string{'\0'}),
// attnotnull
Bool(!column->is_nullable()),
// atthasdef
Bool(column->has_default_value()),
// atthasmissing
Bool(false),
// attidentity
String(std::string{'\0'}),
// attgenerated
String(column->is_generated() ? "s" : std::string{'\0'}),
// attisdropped
Bool(false),
// attislocal
Bool(true),
// attinhcount
Int64(0),
// attcollation
NullPgOid(),
// attoptions
NullString(),
// attfdwoptions
NullString(),
});
}
}
}
for (const View* view : default_schema_->views()) {
if (!view->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "View " << view->Name() << " does not have a PostgreSQL OID.";
continue;
}
int ordinal_position = 1;
for (const View::Column& column : view->columns()) {
const PostgresTypeMapping* pg_type =
system_catalog_->GetTypeFromReverseMapping(column.type);
auto type = pg_type->PostgresTypeOid();
type = type == TEXTOID ? VARCHAROID : type;
type = type == TEXTARRAYOID ? VARCHARARRAYOID : type;
rows.push_back({
// attrelid
CreatePgOidValue(view->postgresql_oid().value()).value(),
// attname
String(column.name),
// atttypid
CreatePgOidValue(type).value(),
// attstattarget
NullInt64(),
// attlen
NullInt64(),
// attnum
Int64(ordinal_position++),
// attndims
Int64(column.type->IsArray() ? 1 : 0),
// attcacheoff
Int64(-1),
// atttypmod
NullInt64(),
// attbyval
NullBool(),
// attalign
NullString(),
// attstorage
NullString(),
// attcompression
String(std::string{'\0'}),
// attnotnull
Bool(false),
// atthasdef
Bool(false),
// atthasmissing
Bool(false),
// attidentity
String(std::string{'\0'}),
// attgenerated
String(std::string{'\0'}),
// attisdropped
Bool(false),
// attislocal
Bool(true),
// attinhcount
Int64(0),
// attcollation
NullPgOid(),
// attoptions
NullString(),
// attfdwoptions
NullString(),
});
}
}
pg_attribute->SetContents(rows);
}
void PGCatalog::FillPGClassTable() {
auto pg_class = tables_by_name_.at(kPGClass).get();
std::vector<std::vector<zetasql::Value>> rows;
// Add tables.
for (const Table* table : default_schema_->tables()) {
const auto& [table_schema_part, table_name_part] =
GetSchemaAndNameForPGCatalog(table->Name());
int namespace_oid = 0;
if (kHardCodedNamedSchemaOid.contains(table_schema_part)) {
namespace_oid = kHardCodedNamedSchemaOid.at(table_schema_part);
} else {
const NamedSchema* named_schema =
default_schema_->FindNamedSchema(table_schema_part);
if (!named_schema->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Named schema " << table_schema_part
<< " does not have a PostgreSQL OID.";
continue;
}
namespace_oid = named_schema->postgresql_oid().value();
}
if (!table->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Table " << table->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
if (!table->primary_key_index_postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "PK for " << table->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
rows.push_back({
// oid
CreatePgOidValue(table->postgresql_oid().value()).value(),
// relname
String(table_name_part),
// relnamespace
CreatePgOidValue(namespace_oid).value(),
// reltype
NullPgOid(),
// reloftype
NullPgOid(),
// relowner
NullPgOid(),
// relam
CreatePgOidValue(75001).value(),
// relfilenode
NullPgOid(),
// reltablespace
NullPgOid(),
// relpages
NullInt64(),
// reltuples
NullDouble(),
// relallvisible
NullInt64(),
// reltoastrelid
NullPgOid(),
// relhasindex
Bool(!table->indexes().empty()),
// relisshared
NullBool(),
// relpersistence
String("p"),
// relkind
String("r"),
// relnatts
Int64(table->columns().size()),
// relchecks
Int64(table->check_constraints().size()),
// relhasrules
NullBool(),
// relhastriggers
NullBool(),
// relhassubclass
NullBool(),
// relrowsecurity
NullBool(),
// relforcerowsecurity
NullBool(),
// relispopulated
Bool(true),
// relreplident
NullString(),
// relispartition
NullBool(),
// relrewrite
NullPgOid(),
// relfrozenxid
NullInt64(),
// relminmxid
NullInt64(),
// reloptions
NullString(),
// relpartbound
NullString(),
});
// Add primary key.
rows.push_back({
// oid
CreatePgOidValue(table->primary_key_index_postgresql_oid().value())
.value(),
// relname
String(PrimaryKeyName(table)),
// relnamespace
CreatePgOidValue(namespace_oid).value(),
// reltype
NullPgOid(),
// reloftype
NullPgOid(),
// relowner
NullPgOid(),
// relam
CreatePgOidValue(75002).value(),
// relfilenode
NullPgOid(),
// reltablespace
NullPgOid(),
// relpages
NullInt64(),
// reltuples
NullDouble(),
// relallvisible
NullInt64(),
// reltoastrelid
NullPgOid(),
// relhasindex
Bool(false),
// relisshared
NullBool(),
// relpersistence
String("p"),
// relkind
String("i"),
// relnatts
Int64(table->primary_key().size()),
// relchecks
Int64(0),
// relhasrules
NullBool(),
// relhastriggers
NullBool(),
// relhassubclass
NullBool(),
// relrowsecurity
NullBool(),
// relforcerowsecurity
NullBool(),
// relispopulated
Bool(true),
// relreplident
NullString(),
// relispartition
NullBool(),
// relrewrite
NullPgOid(),
// relfrozenxid
NullInt64(),
// relminmxid
NullInt64(),
// reloptions
NullString(),
// relpartbound
NullString(),
});
// Add indexes.
for (const Index* index : table->indexes()) {
const auto& [index_schema_part, index_name_part] =
GetSchemaAndNameForPGCatalog(index->Name());
if (!index->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Index " << index->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
rows.push_back({
// oid
CreatePgOidValue(index->postgresql_oid().value()).value(),
// relname
String(index_name_part),
// relnamespace
CreatePgOidValue(namespace_oid).value(),
// reltype
NullPgOid(),
// reloftype
NullPgOid(),
// relowner
NullPgOid(),
// relam
CreatePgOidValue(75002).value(),
// relfilenode
NullPgOid(),
// reltablespace
NullPgOid(),
// relpages
NullInt64(),
// reltuples
NullDouble(),
// relallvisible
NullInt64(),
// reltoastrelid
NullPgOid(),
// relhasindex
Bool(false),
// relisshared
NullBool(),
// relpersistence
String("p"),
// relkind
String("i"),
// relnatts
Int64(index->key_columns().size() + index->stored_columns().size()),
// relchecks
Int64(0),
// relhasrules
NullBool(),
// relhastriggers
NullBool(),
// relhassubclass
NullBool(),
// relrowsecurity
NullBool(),
// relforcerowsecurity
NullBool(),
// relispopulated
Bool(true),
// relreplident
NullString(),
// relispartition
NullBool(),
// relrewrite
NullPgOid(),
// relfrozenxid
NullInt64(),
// relminmxid
NullInt64(),
// reloptions
NullString(),
// relpartbound
NullString(),
});
}
}
// Add sequences.
for (const Sequence* sequence : default_schema_->sequences()) {
if (sequence->is_internal_use()) {
// Skip internal sequences.
continue;
}
const auto& [sequence_schema_part, sequence_name_part] =
GetSchemaAndNameForPGCatalog(sequence->Name());
int namespace_oid = 0;
if (kHardCodedNamedSchemaOid.contains(sequence_schema_part)) {
namespace_oid = kHardCodedNamedSchemaOid.at(sequence_schema_part);
} else {
const NamedSchema* named_schema =
default_schema_->FindNamedSchema(sequence_schema_part);
if (!named_schema->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Named schema " << sequence_schema_part
<< " does not have a PostgreSQL OID.";
continue;
}
namespace_oid = named_schema->postgresql_oid().value();
}
if (!sequence->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Sequence " << sequence->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
rows.push_back({
// oid
CreatePgOidValue(sequence->postgresql_oid().value()).value(),
// relname
String(sequence_name_part),
// relnamespace
CreatePgOidValue(namespace_oid).value(),
// reltype
NullPgOid(),
// reloftype
NullPgOid(),
// relowner
NullPgOid(),
// relam
CreatePgOidValue(0).value(),
// relfilenode
NullPgOid(),
// reltablespace
NullPgOid(),
// relpages
NullInt64(),
// reltuples
NullDouble(),
// relallvisible
NullInt64(),
// reltoastrelid
NullPgOid(),
// relhasindex
Bool(false),
// relisshared
NullBool(),
// relpersistence
String("p"),
// relkind
String("S"),
// relnatts
NullInt64(),
// relchecks
Int64(0),
// relhasrules
NullBool(),
// relhastriggers
NullBool(),
// relhassubclass
NullBool(),
// relrowsecurity
NullBool(),
// relforcerowsecurity
NullBool(),
// relispopulated
Bool(true),
// relreplident
NullString(),
// relispartition
NullBool(),
// relrewrite
NullPgOid(),
// relfrozenxid
NullInt64(),
// relminmxid
NullInt64(),
// reloptions
NullString(),
// relpartbound
NullString(),
});
}
// Add views.
for (const View* view : default_schema_->views()) {
const auto& [view_schema_part, view_name_part] =
GetSchemaAndNameForPGCatalog(view->Name());
int namespace_oid = 0;
if (kHardCodedNamedSchemaOid.contains(view_schema_part)) {
namespace_oid = kHardCodedNamedSchemaOid.at(view_schema_part);
} else {
const NamedSchema* named_schema =
default_schema_->FindNamedSchema(view_schema_part);
if (!named_schema->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Named schema " << view_schema_part
<< " does not have a PostgreSQL OID.";
continue;
}
namespace_oid = named_schema->postgresql_oid().value();
}
if (!view->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "View " << view->Name() << " does not have a PostgreSQL OID.";
continue;
}
rows.push_back({
// oid
CreatePgOidValue(view->postgresql_oid().value()).value(),
// relname
String(view_name_part),
// relnamespace
CreatePgOidValue(namespace_oid).value(),
// reltype
NullPgOid(),
// reloftype
NullPgOid(),
// relowner
NullPgOid(),
// relam
CreatePgOidValue(0).value(),
// relfilenode
NullPgOid(),
// reltablespace
NullPgOid(),
// relpages
NullInt64(),
// reltuples
NullDouble(),
// relallvisible
NullInt64(),
// reltoastrelid
NullPgOid(),
// relhasindex
Bool(false),
// relisshared
NullBool(),
// relpersistence
String("p"),
// relkind
String("v"),
// relnatts
Int64(view->columns().size()),
// relchecks
Int64(0),
// relhasrules
NullBool(),
// relhastriggers
NullBool(),
// relhassubclass
NullBool(),
// relrowsecurity
NullBool(),
// relforcerowsecurity
NullBool(),
// relispopulated
Bool(true),
// relreplident
NullString(),
// relispartition
NullBool(),
// relrewrite
NullPgOid(),
// relfrozenxid
NullInt64(),
// relminmxid
NullInt64(),
// reloptions
NullString(),
// relpartbound
NullString(),
});
}
std::vector<PgClassSystemTableMetadata> system_tables_metadata;
for (const auto& [table_name, metadata] :
info_schema_table_name_to_column_metadata_) {
auto full_table_name = absl::StrCat("information_schema.", table_name);
if (!kHardCodedSystemViewOid.contains(full_table_name)) {
ZETASQL_VLOG(1) << "Missing oid for " << full_table_name;
continue;
}
int table_oid = kHardCodedSystemViewOid.at(full_table_name);
int namespace_oid = kHardCodedNamedSchemaOid.at("information_schema");
system_tables_metadata.push_back(PgClassSystemTableMetadata{
table_name, table_oid, namespace_oid,
/*column_count=*/static_cast<int>(metadata.size()), /*is_view=*/true});
}
for (const auto& [table_name, metadata] :
pg_catalog_table_name_to_column_metadata_) {
int table_oid = 0;
bool is_view;
auto full_table_name = absl::StrCat("pg_catalog.", table_name);
if (kHardCodedSystemViewOid.contains(full_table_name)) {
table_oid = kHardCodedSystemViewOid.at(full_table_name);
is_view = true;
} else if (kHardCodedPgCatalogTableOid.contains(full_table_name)) {
table_oid = kHardCodedPgCatalogTableOid.at(full_table_name);
is_view = false;
} else {
continue;
}
int namespace_oid = kHardCodedNamedSchemaOid.at("pg_catalog");
system_tables_metadata.push_back(PgClassSystemTableMetadata{
table_name, table_oid, namespace_oid,
/*column_count=*/static_cast<int>(metadata.size()), is_view});
}
for (const auto& [uppercase_table_name, metadata] :
spanner_sys_table_name_to_column_metadata_) {
auto table_name = absl::AsciiStrToLower(uppercase_table_name);
auto full_table_name = absl::StrCat("spanner_sys.", table_name);
if (!kHardCodedSystemViewOid.contains(full_table_name)) {
ZETASQL_VLOG(1) << "Missing oid for " << full_table_name;
continue;
}
int table_oid = kHardCodedSystemViewOid.at(full_table_name);
int namespace_oid = kHardCodedNamedSchemaOid.at("spanner_sys");
int column_count = 0;
for (const auto& column_metadata : metadata) {
if (absl::StrContains(column_metadata.spanner_type, "STRUCT")) {
// STRUCT columns are not supported in PG.
continue;
}
++column_count;
}
system_tables_metadata.push_back(PgClassSystemTableMetadata{
table_name, table_oid, namespace_oid, column_count, /*is_view=*/true});
}
for (const auto& metadata : system_tables_metadata) {
rows.push_back({
// oid
CreatePgOidValue(metadata.table_oid).value(),
// relname
String(metadata.table_name),
// relnamespace
CreatePgOidValue(metadata.namespace_oid).value(),
// reltype
NullPgOid(),
// reloftype
NullPgOid(),
// relowner
NullPgOid(),
// relam
CreatePgOidValue(metadata.is_view ? 0 : 75001).value(),
// relfilenode
NullPgOid(),
// reltablespace
NullPgOid(),
// relpages
NullInt64(),
// reltuples
NullDouble(),
// relallvisible
NullInt64(),
// reltoastrelid
NullPgOid(),
// relhasindex
Bool(false),
// relisshared
NullBool(),
// relpersistence
String("p"),
// relkind
String(metadata.is_view ? "v" : "r"),
// relnatts
Int64(metadata.column_count),
// relchecks
Int64(0),
// relhasrules
NullBool(),
// relhastriggers
NullBool(),
// relhassubclass
NullBool(),
// relrowsecurity
NullBool(),
// relforcerowsecurity
NullBool(),
// relispopulated
Bool(true),
// relreplident
NullString(),
// relispartition
NullBool(),
// relrewrite
NullPgOid(),
// relfrozenxid
NullInt64(),
// relminmxid
NullInt64(),
// reloptions
NullString(),
// relpartbound
NullString(),
});
}
pg_class->SetContents(rows);
}
void PGCatalog::FillPGCollationTable() {
auto pg_collation = tables_by_name_.at(kPGCollation).get();
std::vector<std::vector<zetasql::Value>> rows;
for (auto collname : kSupportedCollations) {
auto collation_metadata = GetPgCollationDataFromBootstrap(
PgBootstrapCatalog::Default(), collname);
rows.push_back({
// oid
CreatePgOidValue(collation_metadata->oid()).value(),
// collname
String(collation_metadata->collname()),
// collnamespace
CreatePgOidValue(kHardCodedNamedSchemaOid.at("pg_catalog")).value(),
// collowner
NullPgOid(),
// collcollate
String(collation_metadata->collprovider()),
// collisdeterministic
Bool(collation_metadata->collisdeterministic()),
// collencoding
Int64(collation_metadata->collencoding()),
// collcollate
NullString(),
// collctype
NullString(),
// colliculocale
NullString(),
// collversion
NullString(),
});
}
pg_collation->SetContents(rows);
}
void PGCatalog::FillPGConstraintTable() {
auto pg_constraint = tables_by_name_.at(kPGConstraint).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const Table* table : default_schema_->tables()) {
if (!table->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Table " << table->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
const auto& [table_schema, _] = GetSchemaAndNameForPGCatalog(table->Name());
int namespace_oid = 0;
if (kHardCodedNamedSchemaOid.contains(table_schema)) {
namespace_oid = kHardCodedNamedSchemaOid.at(table_schema);
} else {
const NamedSchema* named_schema =
default_schema_->FindNamedSchema(table_schema);
if (!named_schema->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Named schema " << table_schema
<< " does not have a PostgreSQL OID.";
continue;
}
namespace_oid = named_schema->postgresql_oid().value();
}
int ordinal_position = 1;
std::map<std::string, int> column_name_to_index;
for (const Column* column : table->columns()) {
column_name_to_index[column->Name()] = ordinal_position++;
}
for (const CheckConstraint* check_constraint : table->check_constraints()) {
if (!check_constraint->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Check constraint " << check_constraint->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
std::vector<int64_t> key_attnums;
for (const Column* column : check_constraint->dependent_columns()) {
key_attnums.push_back(column_name_to_index[column->Name()]);
}
rows.push_back({
// oid
CreatePgOidValue(check_constraint->postgresql_oid().value()).value(),
// conname
String(check_constraint->Name()),
// connamespace
CreatePgOidValue(namespace_oid).value(),
// contype
String("c"),
// condeferrable
NullBool(),
// condeferred
NullBool(),
// convalidated
Bool(true),
// conrelid
CreatePgOidValue(table->postgresql_oid().value()).value(),
// contypid
NullPgOid(),
// conindid
NullPgOid(),
// conparentid
NullPgOid(),
// confrelid
CreatePgOidValue(0).value(),
// confupdtype
String(" "),
// confdeltype
String(" "),
// confmatchtype
NullString(),
// conislocal
NullBool(),
// coninhcount
NullInt64(),
// connoinherit
NullBool(),
// conkey
Int64Array(key_attnums),
// confkey
Null(Int64ArrayType()),
// conpfeqop
Null(GetPgOidArrayType()),
// conppeqop
Null(GetPgOidArrayType()),
// conffeqop
Null(GetPgOidArrayType()),
// conexclop
Null(GetPgOidArrayType()),
// conbin
NullString(),
});
}
for (const ForeignKey* foreign_key : table->foreign_keys()) {
if (!foreign_key->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Foreign key " << foreign_key->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
if (!foreign_key->referencing_table()->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Referencing table " <<
foreign_key->referencing_table()->Name() << "of foreign key "
<< foreign_key->Name() << " does not have a PostgreSQL OID.";
continue;
}
std::string on_delete_action;
switch (foreign_key->on_delete_action()) {
case ForeignKey::Action::kNoAction:
on_delete_action = "a";
break;
case ForeignKey::Action::kCascade:
on_delete_action = "c";
break;
default:
on_delete_action = " ";
}
std::vector<int64_t> key_attnums;
for (const Column* column : foreign_key->referencing_columns()) {
key_attnums.push_back(column_name_to_index[column->Name()]);
}
int ordinal_position = 1;
std::map<std::string, int> foreign_column_name_to_index;
for (const Column* column : foreign_key->referenced_table()->columns()) {
foreign_column_name_to_index[column->Name()] = ordinal_position++;
}
std::vector<int64_t> confkey_attnums;
for (const Column* column : foreign_key->referenced_columns()) {
confkey_attnums.push_back(foreign_column_name_to_index[column->Name()]);
}
rows.push_back({
// oid
CreatePgOidValue(foreign_key->postgresql_oid().value()).value(),
// conname
String(foreign_key->Name()),
// connamespace
CreatePgOidValue(namespace_oid).value(),
// contype
String("f"),
// condeferrable
NullBool(),
// condeferred
NullBool(),
// convalidated
Bool(true),
// conrelid
CreatePgOidValue(
foreign_key->referencing_table()->postgresql_oid().value())
.value(),
// contypid
NullPgOid(),
// conindid
NullPgOid(),
// conparentid
NullPgOid(),
// confrelid
CreatePgOidValue(
foreign_key->referenced_table()->postgresql_oid().value())
.value(),
// confupdtype
String(" "), // For update is not yet supported in the emulator.
// confdeltype
String(on_delete_action),
// confmatchtype
NullString(),
// conislocal
NullBool(),
// coninhcount
NullInt64(),
// connoinherit
NullBool(),
// conkey
Int64Array(key_attnums),
// confkey
Int64Array(confkey_attnums),
// conpfeqop
Null(GetPgOidArrayType()),
// conppeqop
Null(GetPgOidArrayType()),
// conffeqop
Null(GetPgOidArrayType()),
// conexclop
Null(GetPgOidArrayType()),
// conbin
NullString(),
});
}
if (!table->primary_key()[0]->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Primary key constraint for table " << table->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
std::vector<int64_t> key_attnums;
for (const KeyColumn* key_column : table->primary_key()) {
key_attnums.push_back(column_name_to_index[key_column->column()->Name()]);
}
// Primary key constraint.
rows.push_back({
// oid
CreatePgOidValue(table->primary_key()[0]->postgresql_oid().value())
.value(),
// conname
String(PrimaryKeyName(table)),
// connamespace
CreatePgOidValue(namespace_oid).value(),
// contype
String("p"),
// condeferrable
NullBool(),
// condeferred
NullBool(),
// convalidated
Bool(true),
// conrelid
CreatePgOidValue(table->postgresql_oid().value()).value(),
// contypid
NullPgOid(),
// conindid
NullPgOid(),
// conparentid
NullPgOid(),
// confrelid
CreatePgOidValue(0).value(),
// confupdtype
String(" "),
// confdeltype
String(" "),
// confmatchtype
NullString(),
// conislocal
NullBool(),
// coninhcount
NullInt64(),
// connoinherit
NullBool(),
// conkey
Int64Array(key_attnums),
// confkey
Null(Int64ArrayType()),
// conpfeqop
Null(GetPgOidArrayType()),
// conppeqop
Null(GetPgOidArrayType()),
// conffeqop
Null(GetPgOidArrayType()),
// conexclop
Null(GetPgOidArrayType()),
// conbin
NullString(),
});
}
pg_constraint->SetContents(rows);
}
void PGCatalog::FillPGIndexTable() {
auto pg_index = tables_by_name_.at(kPGIndex).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const Table* table : default_schema_->tables()) {
// Columns don't track their index in the table, so we need to build a map
// to get the index.
std::map<std::string, int> column_name_to_index;
for (int i = 0; i < table->columns().size(); ++i) {
column_name_to_index[table->columns()[i]->Name()] = i + 1;
}
for (const Index* index : table->indexes()) {
std::vector<int64_t> key_columns;
key_columns.reserve(index->key_columns().size());
for (const auto& key_column : index->key_columns()) {
key_columns.push_back(
column_name_to_index[key_column->column()->Name()]);
}
for (const auto& stored_column : index->stored_columns()) {
key_columns.push_back(
column_name_to_index[stored_column->Name()]);
}
rows.push_back({
// indexrelid
CreatePgOidValue(index->postgresql_oid().value()).value(),
// indrelid
CreatePgOidValue(table->postgresql_oid().value()).value(),
// indnatts
Int64(index->key_columns().size() + index->stored_columns().size()),
// indnkeyatts
Int64(index->key_columns().size()),
// indisunique
Bool(index->is_unique()),
// indisprimary
Bool(false),
// indisexclusion
Bool(false),
// indimmediate
NullBool(),
// indisclustered
Bool(false),
// indisvalid
Bool(true),
// indcheckxmin
Bool(false),
// indisready
Bool(true),
// indislive
Bool(true),
// indisreplident
Bool(false),
// indkey
Int64Array(key_columns),
// indcollation
Null(GetPgOidArrayType()),
// indclass
Null(GetPgOidArrayType()),
// indoption
Null(Int64ArrayType()),
// indexprs
NullString(),
// indpred
NullString(),
});
}
// Add primary key index.
std::vector<int64_t> key_columns;
for (const KeyColumn* key_column : table->primary_key()) {
key_columns.push_back(column_name_to_index[key_column->column()->Name()]);
}
if (!table->primary_key_index_postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Primary key index for table " << table->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
rows.push_back({
// indexrelid
CreatePgOidValue(table->primary_key_index_postgresql_oid().value())
.value(),
// indrelid
CreatePgOidValue(table->postgresql_oid().value()).value(),
// indnatts
Int64(table->primary_key().size()),
// indnkeyatts
Int64(table->primary_key().size()),
// indisunique
Bool(true),
// indisprimary
Bool(true),
// indisexclusion
Bool(false),
// indimmediate
NullBool(),
// indisclustered
Bool(false),
// indisvalid
Bool(true),
// indcheckxmin
Bool(false),
// indisready
Bool(true),
// indislive
Bool(true),
// indisreplident
Bool(false),
// indkey
Int64Array(key_columns),
// indcollation
Null(GetPgOidArrayType()),
// indclass
Null(GetPgOidArrayType()),
// indoption
Null(Int64ArrayType()),
// indexprs
NullString(),
// indpred
NullString(),
});
}
pg_index->SetContents(rows);
}
void PGCatalog::FillPGIndexesTable() {
auto pg_indexes = tables_by_name_.at(kPGIndexes).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const Table* table : default_schema_->tables()) {
const auto& [table_schema, table_name] =
GetSchemaAndNameForPGCatalog(table->Name());
// Add normal indexes.
for (const Index* index : table->indexes()) {
const auto& [index_schema, index_name] =
GetSchemaAndNameForPGCatalog(index->Name());
rows.push_back({
// schemaname
String(table_schema),
// tablename
String(table_name),
// indexname
String(index_name),
// tablespace
NullString(),
// indexdef
NullString(),
});
}
// Add the primary key index.
rows.push_back({
// schemaname
String(table_schema),
// tablename
String(table_name),
// indexname
String(PrimaryKeyName(table)),
// tablespace
NullString(),
// indexdef
NullString(),
});
}
pg_indexes->SetContents(rows);
}
void PGCatalog::FillPGNamespaceTable() {
auto pg_namespace = tables_by_name_.at(kPGNamespace).get();
std::vector<std::vector<zetasql::Value>> rows;
// Add named schemas.
for (const NamedSchema* named_schema : default_schema_->named_schemas()) {
if (!named_schema->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Named schema " << named_schema->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
rows.push_back({
// oid
CreatePgOidValue(named_schema->postgresql_oid().value()).value(),
// schemaname
String(named_schema->Name()),
// namespaceowner
NullPgOid(),
});
}
for (const auto& [name, oid] : kHardCodedNamedSchemaOid) {
rows.push_back({
// oid
CreatePgOidValue(oid).value(),
// nspname
String(name),
// nspowner
NullPgOid(),
});
}
pg_namespace->SetContents(rows);
}
void PGCatalog::FillPGProcTable() {
auto pg_proc = tables_by_name_.at(kPGProc).get();
std::vector<std::vector<zetasql::Value>> rows;
absl::flat_hash_set<const PostgresExtendedFunction*> functions;
auto status = system_catalog_->GetPostgreSQLFunctions(&functions);
if (!status.ok()) {
ZETASQL_VLOG(1) << "Failed to get table-valued functions: " << status;
return;
}
for (const PostgresExtendedFunction* function : functions) {
for (const auto& signature : function->GetPostgresSignatures()) {
auto proc_metadata = GetPgProcDataFromBootstrap(
PgBootstrapCatalog::Default(), signature->postgres_proc_oid());
std::vector<zetasql::Value> proargtypes;
for (const auto& argtype : proc_metadata->proargtypes()) {
proargtypes.push_back(CreatePgOidValue(argtype).value());
}
rows.push_back({
// oid
CreatePgOidValue(proc_metadata->oid()).value(),
// proname
String(proc_metadata->proname()),
// pronamespace
CreatePgOidValue(proc_metadata->pronamespace()).value(),
// proowner
NullPgOid(),
// prolang
NullPgOid(),
// procost
NullDouble(),
// prorows
NullDouble(),
// provariadic
CreatePgOidValue(proc_metadata->provariadic()).value(),
// prokind
String(proc_metadata->prokind()),
// prosecdef
NullBool(),
// proleakproof
NullBool(),
// proisstrict
NullBool(),
// proretset
Bool(proc_metadata->proretset()),
// provolatile
NullString(),
// proparallel
NullString(),
// pronargs
Int64(proc_metadata->pronargs()),
// pronargdefaults
Int64(proc_metadata->pronargdefaults()),
// prorettype
CreatePgOidValue(proc_metadata->prorettype()).value(),
// proargtypes
zetasql::Value::MakeArray(GetPgOidArrayType(), proargtypes).value(),
// proallargtypes
Null(GetPgOidArrayType()),
// proargmodes
Null(StringArrayType()),
// proargnames
Null(StringArrayType()),
// proargdefaults
NullString(),
// protrftypes
Null(GetPgOidArrayType()),
// prosrc
NullString(),
// probin
NullString(),
// prosqlbody
String(proc_metadata->prosqlbody()),
// proconfig
Null(StringArrayType()),
});
}
}
absl::flat_hash_map<Oid, const zetasql::TableValuedFunction*> tvfs;
status = system_catalog_->GetTableValuedFunctions(&tvfs);
if (!status.ok()) {
ZETASQL_VLOG(1) << "Failed to get table-valued functions: " << status;
return;
}
for (const auto& [tvf_oid, tvf] : tvfs) {
auto signature = tvf->GetSignature(0);
std::vector<zetasql::Value> proargtypes;
int variadic_type_oid = 0;
if (!signature->result_type().IsRelation()) {
ZETASQL_VLOG(1) << "Table-valued functions must return a relation type.";
continue;
}
auto& rettype_options = signature->result_type().options();
if (rettype_options.has_relation_input_schema() &&
rettype_options.relation_input_schema().num_columns() != 1) {
ZETASQL_VLOG(1) << "Table-valued functions must return a relation type "
<< "with exactly one column.";
continue;
}
auto rettype_mapping = system_catalog_->GetTypeFromReverseMapping(
rettype_options.relation_input_schema().column(0).type);
if (rettype_mapping == nullptr) {
ZETASQL_VLOG(1) << "Failed to get type mapping for "
<< signature->result_type().DebugString();
continue;
}
int rettype_oid = rettype_mapping->PostgresTypeOid();
for (const auto& arg : signature->arguments()) {
auto type_mapping =
system_catalog_->GetTypeFromReverseMapping(arg.type());
if (type_mapping == nullptr) {
ZETASQL_VLOG(1) << "Failed to get type mapping for "
<< arg.type()->DebugString();
continue;
}
if (arg.repeated()) {
variadic_type_oid = type_mapping->PostgresTypeOid();
}
proargtypes.push_back(
CreatePgOidValue(type_mapping->PostgresTypeOid()).value());
}
rows.push_back({
// oid
CreatePgOidValue(tvf_oid).value(),
// proname
String(tvf->Name()),
// pronamespace
CreatePgOidValue(50000).value(),
// proowner
NullPgOid(),
// prolang
NullPgOid(),
// procost
NullDouble(),
// prorows
NullDouble(),
// provariadic
CreatePgOidValue(variadic_type_oid).value(),
// prokind
String("f"),
// prosecdef
NullBool(),
// proleakproof
NullBool(),
// proisstrict
NullBool(),
// proretset
Bool(true),
// provolatile
NullString(),
// proparallel
NullString(),
// pronargs
Int64(signature->arguments().size()),
// pronargdefaults
Int64(0),
// prorettype
CreatePgOidValue(rettype_oid).value(),
// proargtypes
zetasql::Value::MakeArray(GetPgOidArrayType(), proargtypes).value(),
// proallargtypes
Null(GetPgOidArrayType()),
// proargmodes
Null(StringArrayType()),
// proargnames
Null(StringArrayType()),
// proargdefaults
NullString(),
// protrftypes
Null(GetPgOidArrayType()),
// prosrc
NullString(),
// probin
NullString(),
// prosqlbody
NullString(),
// proconfig
Null(StringArrayType()),
});
}
absl::flat_hash_set<const zetasql::TableValuedFunction*> changestream_tvfs;
status = root_catalog_->GetTableValuedFunctions(&changestream_tvfs);
if (!status.ok()) {
ZETASQL_VLOG(1) << "Failed to get table-valued functions from root catalog: "
<< status;
return;
}
absl::flat_hash_map<std::string, const zetasql::TableValuedFunction*>
changestream_tvfs_map;
for (const zetasql::TableValuedFunction* tvf : changestream_tvfs) {
changestream_tvfs_map[tvf->Name()] = tvf;
}
for (const ChangeStream* change_stream : default_schema_->change_streams()) {
if (!changestream_tvfs_map.contains(change_stream->tvf_name())) {
ZETASQL_VLOG(1) << "Change stream " << change_stream->Name()
<< " does not have a table-valued function.";
continue;
}
if (!change_stream->tvf_postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Change stream TVF " << change_stream->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
const auto& [change_stream_schema, change_stream_name] =
GetSchemaAndNameForPGCatalog(change_stream->Name());
int namespace_oid = 0;
if (kHardCodedNamedSchemaOid.contains(change_stream_schema)) {
namespace_oid = kHardCodedNamedSchemaOid.at(change_stream_schema);
} else {
const NamedSchema* named_schema =
default_schema_->FindNamedSchema(change_stream_schema);
if (!named_schema->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Named schema " << change_stream_schema
<< " does not have a PostgreSQL OID.";
continue;
}
namespace_oid = named_schema->postgresql_oid().value();
}
auto signature =
changestream_tvfs_map[change_stream->tvf_name()]->GetSignature(0);
std::vector<zetasql::Value> proargtypes;
int variadic_type_oid = 0;
if (!signature->result_type().IsRelation()) {
ZETASQL_VLOG(1) << "Table-valued functions must return a relation type.";
continue;
}
auto& rettype_options = signature->result_type().options();
if (rettype_options.has_relation_input_schema() &&
rettype_options.relation_input_schema().num_columns() != 1) {
ZETASQL_VLOG(1) << "Table-valued functions must return a relation type "
<< "with exactly one column.";
continue;
}
auto rettype_mapping = system_catalog_->GetTypeFromReverseMapping(
rettype_options.relation_input_schema().column(0).type);
if (rettype_mapping == nullptr) {
ZETASQL_VLOG(1) << "Failed to get type mapping for "
<< signature->result_type().DebugString();
continue;
}
int rettype_oid = rettype_mapping->PostgresTypeOid();
for (const auto& arg : signature->arguments()) {
auto type_mapping =
system_catalog_->GetTypeFromReverseMapping(arg.type());
if (type_mapping == nullptr) {
ZETASQL_VLOG(1) << "Failed to get type mapping for "
<< arg.type()->DebugString();
continue;
} else if (type_mapping->PostgresTypeOid() == TEXTOID) {
type_mapping = types::PgVarcharMapping();
} else if (type_mapping->PostgresTypeOid() == TEXTARRAYOID) {
type_mapping = types::PgVarcharArrayMapping();
}
if (arg.repeated()) {
variadic_type_oid = type_mapping->PostgresTypeOid();
}
proargtypes.push_back(
CreatePgOidValue(type_mapping->PostgresTypeOid()).value());
}
rows.push_back({
// oid
CreatePgOidValue(change_stream->tvf_postgresql_oid().value()).value(),
// proname
String(change_stream->tvf_name()),
// pronamespace
CreatePgOidValue(namespace_oid).value(),
// proowner
NullPgOid(),
// prolang
NullPgOid(),
// procost
NullDouble(),
// prorows
NullDouble(),
// provariadic
CreatePgOidValue(variadic_type_oid).value(),
// prokind
String("f"),
// prosecdef
NullBool(),
// proleakproof
NullBool(),
// proisstrict
NullBool(),
// proretset
Bool(true),
// provolatile
NullString(),
// proparallel
NullString(),
// pronargs
Int64(signature->arguments().size()),
// pronargdefaults
Int64(0),
// prorettype
CreatePgOidValue(rettype_oid).value(),
// proargtypes
zetasql::Value::MakeArray(GetPgOidArrayType(), proargtypes).value(),
// proallargtypes
Null(GetPgOidArrayType()),
// proargmodes
Null(StringArrayType()),
// proargnames
Null(StringArrayType()),
// proargdefaults
NullString(),
// protrftypes
Null(GetPgOidArrayType()),
// prosrc
NullString(),
// probin
NullString(),
// prosqlbody
NullString(),
// proconfig
Null(StringArrayType()),
});
}
pg_proc->SetContents(rows);
}
void PGCatalog::FillPGSequenceTable() {
auto pg_sequence = tables_by_name_.at(kPGSequence).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const Sequence* sequence : default_schema_->sequences()) {
if (sequence->is_internal_use()) {
// Skip internal sequences.
continue;
}
if (!sequence->postgresql_oid().has_value()) {
ZETASQL_VLOG(1) << "Sequence " << sequence->Name()
<< " does not have a PostgreSQL OID.";
continue;
}
rows.push_back({
// seqrelid
CreatePgOidValue(sequence->postgresql_oid().value()).value(),
// seqtypid
CreatePgOidValue(20).value(), // Only bigint is supported.
// seqstart
Int64(sequence->start_with_counter().value()),
// seqincrement
NullInt64(),
// seqmax
NullInt64(),
// seqmin
NullInt64(),
// seqcache
Int64(1000),
// seqcycle
Bool(false),
});
}
pg_sequence->SetContents(rows);
}
void PGCatalog::FillPGSequencesTable() {
auto pg_sequences = tables_by_name_.at(kPGSequences).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const Sequence* sequence : default_schema_->sequences()) {
if (sequence->is_internal_use()) {
// Skip internal sequences.
continue;
}
const auto& [sequence_schema_part, sequence_name_part] =
GetSchemaAndNameForPGCatalog(sequence->Name());
rows.push_back({
// schemaname
String(sequence_schema_part),
// sequencename
String(sequence_name_part),
// sequenceowner
NullString(),
// start_value
Int64(sequence->start_with_counter().value()),
// min_value
NullInt64(),
// max_value
NullInt64(),
// increment_by
NullInt64(),
// cycle
Bool(false),
// cache_size
Int64(1000),
// last_value
NullInt64(),
});
}
pg_sequences->SetContents(rows);
}
void PGCatalog::FillPGSettingsTable() {
auto pg_settings = tables_by_name_.at(kPGSettings).get();
std::vector<std::vector<zetasql::Value>> rows;
std::vector<std::string> enumvals;
rows.push_back({
// name
String("max_index_keys"),
// setting
String("16"),
// unit
NullString(),
// category
String("Preset Options"),
// short_desc
String("Shows the maximum number of index keys."),
// extra_desc
NullString(),
// context
String("internal"),
// vartype
String("integer"),
// source
String("default"),
// min_val
String("16"),
// max_val
String("16"),
// enumvals
StringArray(enumvals),
// boot_val
String("16"),
// reset_val
String("16"),
// sourcefile
NullString(),
// sourceline
NullInt64(),
// pending_restart
Bool(false),
});
pg_settings->SetContents(rows);
}
void PGCatalog::FillPGTablesTable() {
auto pg_tables = tables_by_name_.at(kPGTables).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const Table* table : default_schema_->tables()) {
const auto& [table_schema, table_name] =
GetSchemaAndNameForPGCatalog(table->Name());
rows.push_back({
// schemaname
String(table_schema),
// tablename
String(table_name),
// tableowner
NullString(),
// tablespace
NullString(),
// hasindexes
Bool(!table->indexes().empty()),
// hasrules
NullBool(),
// hastriggers
NullBool(),
// rowsecurity
NullBool(),
});
}
pg_tables->SetContents(rows);
}
void PGCatalog::FillPGTypeTable() {
auto pg_type = tables_by_name_.at(kPGType).get();
std::vector<std::vector<zetasql::Value>> rows;
absl::flat_hash_set<const PostgresTypeMapping*> postgres_types;
auto status = system_catalog_->GetPostgreSQLTypes(&postgres_types);
for (const PostgresTypeMapping* postgres_type : postgres_types) {
auto type_metadata = GetPgTypeDataFromBootstrap(
PgBootstrapCatalog::Default(), postgres_type->PostgresTypeOid());
rows.push_back({
// oid
CreatePgOidValue(postgres_type->PostgresTypeOid()).value(),
// typname
String(postgres_type->raw_type_name()),
// typnamespace
CreatePgOidValue(11).value(),
// typowner
NullPgOid(),
// typlen
Int64(type_metadata->typlen()),
// typbyval
Bool(type_metadata->typbyval()),
// typtype
String(type_metadata->typtype()),
// typcategory
String(type_metadata->typcategory()),
// typispreferred
Bool(type_metadata->typispreferred()),
// typisdefined
Bool(type_metadata->typisdefined()),
// typdelim
String(type_metadata->typdelim()),
// typrelid
CreatePgOidValue(0).value(),
// typelem
CreatePgOidValue(type_metadata->typelem()).value(),
// typarray
CreatePgOidValue(type_metadata->typarray()).value(),
// typalign
NullString(),
// typstorage
NullString(),
// typnotnull
NullBool(),
// typbasetype
NullPgOid(),
// typtypmod
NullInt64(),
// typndims
NullInt64(),
// typcollation
NullPgOid(),
// typdefaultbin
NullString(),
// typdefault
NullString(),
});
}
for (uint32_t pseudotype_oid : {
2276, // any
2277, // anyarray
2283, // anyelement
2776, // anynonarray
5078, // anycompatiblearray
}) {
auto type_metadata = GetPgTypeDataFromBootstrap(
PgBootstrapCatalog::Default(), pseudotype_oid);
rows.push_back({
// oid
CreatePgOidValue(pseudotype_oid).value(),
// typname
String(type_metadata->typname()),
// typnamespace
CreatePgOidValue(11).value(),
// typowner
NullPgOid(),
// typlen
Int64(type_metadata->typlen()),
// typbyval
Bool(type_metadata->typbyval()),
// typtype
String(type_metadata->typtype()),
// typcategory
String(type_metadata->typcategory()),
// typispreferred
Bool(type_metadata->typispreferred()),
// typisdefined
Bool(type_metadata->typisdefined()),
// typdelim
String(type_metadata->typdelim()),
// typrelid
CreatePgOidValue(0).value(),
// typelem
CreatePgOidValue(type_metadata->typelem()).value(),
// typarray
CreatePgOidValue(type_metadata->typarray()).value(),
// typalign
NullString(),
// typstorage
NullString(),
// typnotnull
NullBool(),
// typbasetype
NullPgOid(),
// typtypmod
NullInt64(),
// typndims
NullInt64(),
// typcollation
NullPgOid(),
// typdefaultbin
NullString(),
// typdefault
NullString(),
});
}
pg_type->SetContents(rows);
}
void PGCatalog::FillPGViewsTable() {
auto pg_views = tables_by_name_.at(kPGViews).get();
std::vector<std::vector<zetasql::Value>> rows;
for (const View* view : default_schema_->views()) {
const auto& [view_schema, view_name] =
GetSchemaAndNameForPGCatalog(view->Name());
rows.push_back({
// schemaname
String(view_schema),
// viewname
String(view_name),
// viewowner
NullString(),
// definition
String(view->body_origin().value()),
});
}
for (const auto& [table_name, metadata] :
info_schema_table_name_to_column_metadata_) {
rows.push_back({
// schemaname
String("information_schema"),
// viewname
String(table_name),
// viewowner
NullString(),
// definition
NullString(),
});
}
for (const auto& [table_name, metadata] :
pg_catalog_table_name_to_column_metadata_) {
rows.push_back({
// schemaname
String("pg_catalog"),
// viewname
String(table_name),
// viewowner
NullString(),
// definition
NullString(),
});
}
for (const auto& [table_name, metadata] :
spanner_sys_table_name_to_column_metadata_) {
rows.push_back({
// schemaname
String("spanner_sys"),
// viewname
String(table_name),
// viewowner
NullString(),
// definition
NullString(),
});
}
pg_views->SetContents(rows);
}
} // namespace postgres_translator