From 2c505000dd21e02c908191b3f12f1fb10c9d0b28 Mon Sep 17 00:00:00 2001 From: Arjun Lall Date: Thu, 9 Jan 2025 01:41:22 -0800 Subject: [PATCH] Refactor PG table definitions and fix types --- src/parser_table.go | 258 +++++++++------------------ src/parser_utils.go | 83 ++++----- src/pg_constants.go | 338 ++++++++++++++++++++++++++++++++++++ src/query_handler_test.go | 38 ++-- src/query_remapper_table.go | 122 +------------ 5 files changed, 488 insertions(+), 351 deletions(-) diff --git a/src/parser_table.go b/src/parser_table.go index bd43d25..f213b43 100644 --- a/src/parser_table.go +++ b/src/parser_table.go @@ -28,109 +28,98 @@ func (parser *ParserTable) NodeToQuerySchemaTable(node *pgQuery.Node) QuerySchem } } -func (parser *ParserTable) MakeEmptyTableNode(tableName string, columns []string, alias string) *pgQuery.Node { - return parser.utils.MakeSubselectWithoutRowsNode(tableName, columns, alias) +func (parser *ParserTable) MakeEmptyTableNode(tableName string, tableDef TableDefinition, alias string) *pgQuery.Node { + return parser.utils.MakeSubselectWithoutRowsNode(tableName, tableDef, alias) } // pg_catalog.pg_shadow -> VALUES(values...) t(columns...) func (parser *ParserTable) MakePgShadowNode(user string, encryptedPassword string, alias string) *pgQuery.Node { - columns := PG_SHADOW_VALUE_BY_COLUMN.Keys() - staticRowValues := PG_SHADOW_VALUE_BY_COLUMN.Values() + tableDef := PG_SHADOW_DEFINITION + values := tableDef.Values - var rowsValues [][]string - - rowValues := make([]string, len(staticRowValues)) - copy(rowValues, staticRowValues) - for i, column := range columns { - switch column { + for i, col := range tableDef.Columns { + switch col.Name { case "usename": - rowValues[i] = user + values[i] = user case "passwd": - rowValues[i] = encryptedPassword + values[i] = encryptedPassword } } - rowsValues = append(rowsValues, rowValues) - return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_SHADOW, columns, rowsValues, alias) + return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_SHADOW, tableDef, [][]string{values}, alias) } // pg_catalog.pg_roles -> VALUES(values...) t(columns...) func (parser *ParserTable) MakePgRolesNode(user string, alias string) *pgQuery.Node { - columns := PG_ROLES_VALUE_BY_COLUMN.Keys() - staticRowValues := PG_ROLES_VALUE_BY_COLUMN.Values() - - var rowsValues [][]string - rowValues := make([]string, len(staticRowValues)) - copy(rowValues, staticRowValues) + tableDef := PG_ROLES_DEFINITION + values := tableDef.Values - for i, column := range columns { - if column == "rolname" { - rowValues[i] = user + for i, col := range tableDef.Columns { + if col.Name == "rolname" { + values[i] = user + break } } - rowsValues = append(rowsValues, rowValues) - return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_ROLES, columns, rowsValues, alias) + return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_ROLES, tableDef, [][]string{values}, alias) } // pg_catalog.pg_extension -> VALUES(values...) t(columns...) func (parser *ParserTable) MakePgExtensionNode(alias string) *pgQuery.Node { - columns := PG_EXTENSION_VALUE_BY_COLUMN.Keys() - staticRowValues := PG_EXTENSION_VALUE_BY_COLUMN.Values() - rowsValues := [][]string{staticRowValues} - return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_EXTENSION, columns, rowsValues, alias) + tableDef := PG_EXTENSION_DEFINITION + return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_EXTENSION, tableDef, [][]string{tableDef.Values}, alias) } // pg_catalog.pg_database -> VALUES(values...) t(columns...) func (parser *ParserTable) MakePgDatabaseNode(database string, alias string) *pgQuery.Node { - columns := PG_DATABASE_VALUE_BY_COLUMN.Keys() - staticRowValues := PG_DATABASE_VALUE_BY_COLUMN.Values() + tableDef := PG_DATABASE_DEFINITION + values := tableDef.Values - var rowsValues [][]string - rowValues := make([]string, len(staticRowValues)) - copy(rowValues, staticRowValues) - for i, column := range columns { - if column == "datname" { - rowValues[i] = database + for i, col := range tableDef.Columns { + if col.Name == "datname" { + values[i] = database + break } } - rowsValues = append(rowsValues, rowValues) - return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_DATABASE, columns, rowsValues, alias) + return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_DATABASE, tableDef, [][]string{values}, alias) } // pg_catalog.pg_user -> VALUES(values...) t(columns...) func (parser *ParserTable) MakePgUserNode(user string, alias string) *pgQuery.Node { - columns := PG_USER_VALUE_BY_COLUMN.Keys() - rowValues := PG_USER_VALUE_BY_COLUMN.Values() + tableDef := PG_USER_DEFINITION + values := tableDef.Values - rowValues[0] = user + for i, col := range tableDef.Columns { + if col.Name == "usename" { + values[i] = user + break + } + } - return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_USER, columns, [][]string{rowValues}, alias) + return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_USER, tableDef, [][]string{values}, alias) } // pg_catalog.pg_stat_user_tables -> VALUES(values...) t(columns...) func (parser *ParserTable) MakePgStatUserTablesNode(schemaTables []IcebergSchemaTable, alias string) *pgQuery.Node { - columns := PG_STAT_USER_TABLES_VALUE_BY_COLUMN.Keys() - staticRowValues := PG_STAT_USER_TABLES_VALUE_BY_COLUMN.Values() - + tableDef := PG_STAT_USER_TABLES_DEFINITION var rowsValues [][]string for _, schemaTable := range schemaTables { - rowValues := make([]string, len(staticRowValues)) - copy(rowValues, staticRowValues) - for i, column := range columns { - switch column { + values := tableDef.Values + + for i, col := range tableDef.Columns { + switch col.Name { case "schemaname": - rowValues[i] = schemaTable.Schema + values[i] = schemaTable.Schema case "relname": - rowValues[i] = schemaTable.Table + values[i] = schemaTable.Table } } - rowsValues = append(rowsValues, rowValues) + rowsValues = append(rowsValues, values) } - return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_STAT_USER_TABLES, columns, rowsValues, alias) + return parser.utils.MakeSubselectWithRowsNode(PG_TABLE_PG_STAT_USER_TABLES, tableDef, rowsValues, alias) } // Other information_schema.* tables @@ -192,9 +181,17 @@ func (parser *ParserTable) SchemaFunction(node *pgQuery.Node) PgSchemaFunction { // pg_catalog.pg_get_keywords() -> VALUES(values...) t(columns...) func (parser *ParserTable) MakePgGetKeywordsNode(node *pgQuery.Node) *pgQuery.Node { - columns := []string{"word", "catcode", "barelabel", "catdesc", "baredesc"} + tableDef := TableDefinition{ + Columns: []ColumnDefinition{ + {"word", "text"}, + {"catcode", "text"}, + {"barelabel", "text"}, + {"catdesc", "text"}, + {"baredesc", "text"}, + }, + } - var rows [][]string + var rowsValues [][]string for _, kw := range DUCKDB_KEYWORDS { catcode := "U" catdesc := "unreserved" @@ -211,14 +208,13 @@ func (parser *ParserTable) MakePgGetKeywordsNode(node *pgQuery.Node) *pgQuery.No catdesc = "unreserved (cannot be function or type name)" } - row := []string{ + rowsValues = append(rowsValues, []string{ kw.word, catcode, "t", catdesc, "can be bare label", - } - rows = append(rows, row) + }) } var alias string @@ -226,25 +222,7 @@ func (parser *ParserTable) MakePgGetKeywordsNode(node *pgQuery.Node) *pgQuery.No alias = node.GetAlias().Aliasname } - return parser.utils.MakeSubselectWithRowsNode(PG_FUNCTION_PG_GET_KEYWORDS, columns, rows, alias) -} - -// array_upper(array, 1) -> len(array) -func (parser *ParserTable) MakeArrayUpperNode(funcCallNode *pgQuery.FuncCall) *pgQuery.FuncCall { - dimension := funcCallNode.Args[1].GetAConst().GetIval().Ival - if dimension != 1 { - return funcCallNode - } - - return pgQuery.MakeFuncCallNode( - []*pgQuery.Node{ - pgQuery.MakeStrNode("len"), - }, - []*pgQuery.Node{ - funcCallNode.Args[0], - }, - 0, - ).GetFuncCall() + return parser.utils.MakeSubselectWithRowsNode(PG_FUNCTION_PG_GET_KEYWORDS, tableDef, rowsValues, alias) } // pg_catalog.pg_show_all_settings() -> duckdb_settings() mapped to pg format @@ -358,6 +336,12 @@ func (parser *ParserTable) MakePgShowAllSettingsNode(node *pgQuery.Node) *pgQuer // pg_catalog.pg_is_in_recovery() -> 'f'::bool func (parser *ParserTable) MakePgIsInRecoveryNode(node *pgQuery.Node) *pgQuery.Node { + tableDef := TableDefinition{ + Columns: []ColumnDefinition{ + {"pg_is_in_recovery", "bool"}, + }, + } + var alias string if node.GetAlias() != nil { alias = node.GetAlias().Aliasname @@ -365,111 +349,29 @@ func (parser *ParserTable) MakePgIsInRecoveryNode(node *pgQuery.Node) *pgQuery.N return parser.utils.MakeSubselectWithRowsNode( PG_FUNCTION_PG_IS_IN_RECOVERY, - []string{"pg_is_in_recovery"}, + tableDef, [][]string{{"f"}}, alias, ) } -var PG_SHADOW_VALUE_BY_COLUMN = NewOrderedMap([][]string{ - {"usename", "bemidb"}, - {"usesysid", "10"}, - {"usecreatedb", "FALSE"}, - {"usesuper", "FALSE"}, - {"userepl", "TRUE"}, - {"usebypassrls", "FALSE"}, - {"passwd", ""}, - {"valuntil", "NULL"}, - {"useconfig", "NULL"}, -}) - -var PG_ROLES_VALUE_BY_COLUMN = NewOrderedMap([][]string{ - {"oid", "10"}, - {"rolname", ""}, - {"rolsuper", "true"}, - {"rolinherit", "true"}, - {"rolcreaterole", "true"}, - {"rolcreatedb", "true"}, - {"rolcanlogin", "true"}, - {"rolreplication", "false"}, - {"rolconnlimit", "-1"}, - {"rolpassword", "NULL"}, - {"rolvaliduntil", "NULL"}, - {"rolbypassrls", "false"}, - {"rolconfig", "NULL"}, -}) - -var PG_EXTENSION_VALUE_BY_COLUMN = NewOrderedMap([][]string{ - {"oid", "13823"}, - {"extname", "plpgsql"}, - {"extowner", "10"}, - {"extnamespace", "11"}, - {"extrelocatable", "false"}, - {"extversion", "1.0"}, - {"extconfig", "NULL"}, - {"extcondition", "NULL"}, -}) - -var PG_DATABASE_VALUE_BY_COLUMN = NewOrderedMap([][]string{ - {"oid", "16388"}, - {"datname", "bemidb"}, - {"datdba", "10"}, - {"encoding", "6"}, - {"datlocprovider", "c"}, - {"datistemplate", "FALSE"}, - {"datallowconn", "TRUE"}, - {"datconnlimit", "-1"}, - {"datfrozenxid", "722"}, - {"datminmxid", "1"}, - {"dattablespace", "1663"}, - {"datcollate", "en_US.UTF-8"}, - {"datctype", "en_US.UTF-8"}, - {"datlocale", "NULL"}, - {"daticurules", "NULL"}, - {"datcollversion", "NULL"}, - {"datacl", "NULL"}, -}) - -var PG_USER_VALUE_BY_COLUMN = NewOrderedMap([][]string{ - {"usename", "bemidb"}, - {"usesysid", "10"}, - {"usecreatedb", "t"}, - {"usesuper", "t"}, - {"userepl", "t"}, - {"usebypassrls", "t"}, - {"passwd", ""}, - {"valuntil", "NULL"}, - {"useconfig", "NULL"}, -}) - -var PG_STAT_USER_TABLES_VALUE_BY_COLUMN = NewOrderedMap([][]string{ - {"relid", "123456"}, - {"schemaname", "public"}, - {"relname", "table"}, - {"seq_scan", "0"}, - {"last_seq_scan", "NULL"}, - {"seq_tup_read", "0"}, - {"idx_scan", "0"}, - {"last_idx_scan", "NULL"}, - {"idx_tup_fetch", "0"}, - {"n_tup_ins", "0"}, - {"n_tup_upd", "0"}, - {"n_tup_del", "0"}, - {"n_tup_hot_upd", "0"}, - {"n_tup_newpage_upd", "0"}, - {"n_live_tup", "1"}, - {"n_dead_tup", "0"}, - {"n_mod_since_analyze", "0"}, - {"n_ins_since_vacuum", "0"}, - {"last_vacuum", "NULL"}, - {"last_autovacuum", "NULL"}, - {"last_analyze", "NULL"}, - {"last_autoanalyze", "NULL"}, - {"vacuum_count", "0"}, - {"autovacuum_count", "0"}, - {"analyze_count", "0"}, - {"autoanalyze_count", "0"}, -}) +// array_upper(array, 1) -> len(array) +func (parser *ParserTable) MakeArrayUpperNode(funcCallNode *pgQuery.FuncCall) *pgQuery.FuncCall { + dimension := funcCallNode.Args[1].GetAConst().GetIval().Ival + if dimension != 1 { + return funcCallNode + } + + return pgQuery.MakeFuncCallNode( + []*pgQuery.Node{ + pgQuery.MakeStrNode("len"), + }, + []*pgQuery.Node{ + funcCallNode.Args[0], + }, + 0, + ).GetFuncCall() +} type DuckDBKeyword struct { word string diff --git a/src/parser_utils.go b/src/parser_utils.go index e40673f..32af04d 100644 --- a/src/parser_utils.go +++ b/src/parser_utils.go @@ -1,9 +1,6 @@ package main import ( - "strconv" - "strings" - pgQuery "github.com/pganalyze/pg_query_go/v5" ) @@ -32,40 +29,20 @@ func (utils *ParserUtils) SchemaFunction(functionCall *pgQuery.FuncCall) PgSchem } } -func (utils *ParserUtils) MakeSubselectWithRowsNode(tableName string, columns []string, rowsValues [][]string, alias string) *pgQuery.Node { - parserType := NewParserType(utils.config) - - columnNodes := make([]*pgQuery.Node, len(columns)) - for i, column := range columns { - columnNodes[i] = pgQuery.MakeStrNode(column) +func (utils *ParserUtils) MakeSubselectWithRowsNode(tableName string, tableDef TableDefinition, rowsValues [][]string, alias string) *pgQuery.Node { + columnNodes := make([]*pgQuery.Node, len(tableDef.Columns)) + for i, col := range tableDef.Columns { + columnNodes[i] = pgQuery.MakeStrNode(col.Name) } selectStmt := &pgQuery.SelectStmt{} for _, row := range rowsValues { var rowList []*pgQuery.Node - for _, val := range row { - if val == "NULL" { - constNode := &pgQuery.Node{ - Node: &pgQuery.Node_AConst{ - AConst: &pgQuery.A_Const{ - Isnull: true, - }, - }, - } - rowList = append(rowList, constNode) - } else { - constNode := pgQuery.MakeAConstStrNode(val, 0) - if _, err := strconv.ParseInt(val, 10, 64); err == nil { - constNode = parserType.MakeCaseTypeCastNode(constNode, "int8") - } else { - valLower := strings.ToLower(val) - if valLower == "true" || valLower == "false" { - constNode = parserType.MakeCaseTypeCastNode(constNode, "bool") - } - } - rowList = append(rowList, constNode) - } + for i, val := range row { + colType := tableDef.Columns[i].Type + constNode := utils.makeTypedConstNode(val, colType) + rowList = append(rowList, constNode) } selectStmt.ValuesLists = append(selectStmt.ValuesLists, &pgQuery.Node{Node: &pgQuery.Node_List{List: &pgQuery.List{Items: rowList}}}) @@ -92,18 +69,24 @@ func (utils *ParserUtils) MakeSubselectWithRowsNode(tableName string, columns [] } } -func (utils *ParserUtils) MakeSubselectWithoutRowsNode(tableName string, columns []string, alias string) *pgQuery.Node { - columnNodes := make([]*pgQuery.Node, len(columns)) - for i, column := range columns { - columnNodes[i] = pgQuery.MakeStrNode(column) +func (utils *ParserUtils) MakeSubselectWithoutRowsNode(tableName string, tableDef TableDefinition, alias string) *pgQuery.Node { + parserType := NewParserType(utils.config) + columnNodes := make([]*pgQuery.Node, len(tableDef.Columns)) + for i, col := range tableDef.Columns { + columnNodes[i] = pgQuery.MakeStrNode(col.Name) } - targetList := make([]*pgQuery.Node, len(columns)) - for i := range columns { - targetList[i] = pgQuery.MakeResTargetNodeWithVal( - utils.MakeAConstBoolNode(false), - 0, - ) + targetList := make([]*pgQuery.Node, len(tableDef.Columns)) + for i, col := range tableDef.Columns { + nullNode := &pgQuery.Node{ + Node: &pgQuery.Node_AConst{ + AConst: &pgQuery.A_Const{ + Isnull: true, + }, + }, + } + typedNullNode := parserType.MakeTypeCastNode(nullNode, col.Type) + targetList[i] = pgQuery.MakeResTargetNodeWithVal(typedNullNode, 0) } if alias == "" { @@ -169,3 +152,21 @@ func (utils *ParserUtils) MakeAConstBoolNode(val bool) *pgQuery.Node { }, } } + +func (utils *ParserUtils) makeTypedConstNode(val string, pgType string) *pgQuery.Node { + parserType := NewParserType(utils.config) + + if val == "NULL" { + return &pgQuery.Node{ + Node: &pgQuery.Node_AConst{ + AConst: &pgQuery.A_Const{ + Isnull: true, + }, + }, + } + } + + constNode := pgQuery.MakeAConstStrNode(val, 0) + + return parserType.MakeTypeCastNode(constNode, pgType) +} diff --git a/src/pg_constants.go b/src/pg_constants.go index 7d301ce..3576bca 100644 --- a/src/pg_constants.go +++ b/src/pg_constants.go @@ -38,6 +38,344 @@ const ( PG_VAR_SEARCH_PATH = "search_path" ) +type ColumnDefinition struct { + Name string + Type string +} + +type TableDefinition struct { + Columns []ColumnDefinition + Values []string +} + +var PG_INHERITS_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"inhrelid", "oid"}, + {"inhparent", "oid"}, + {"inhseqno", "int4"}, + {"inhdetachpending", "bool"}, + }, +} + +var PG_SHDESCRIPTION_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"objoid", "oid"}, + {"classoid", "oid"}, + {"description", "text"}, + }, +} + +var PG_STATIO_USER_TABLES_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"relid", "oid"}, + {"schemaname", "text"}, + {"relname", "text"}, + {"heap_blks_read", "int8"}, + {"heap_blks_hit", "int8"}, + {"idx_blks_read", "int8"}, + {"idx_blks_hit", "int8"}, + {"toast_blks_read", "int8"}, + {"toast_blks_hit", "int8"}, + {"tidx_blks_read", "int8"}, + {"tidx_blks_hit", "int8"}, + }, +} + +var PG_REPLICATION_SLOTS_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"slot_name", "text"}, + {"plugin", "text"}, + {"slot_type", "text"}, + {"datoid", "oid"}, + {"database", "text"}, + {"temporary", "bool"}, + {"active", "bool"}, + {"active_pid", "int4"}, + {"xmin", "int8"}, + {"catalog_xmin", "int8"}, + {"restart_lsn", "text"}, + {"confirmed_flush_lsn", "text"}, + {"wal_status", "text"}, + {"safe_wal_size", "int8"}, + {"two_phase", "bool"}, + {"conflicting", "bool"}, + }, +} + +var PG_SHADOW_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"usename", "text"}, + {"usesysid", "oid"}, + {"usecreatedb", "bool"}, + {"usesuper", "bool"}, + {"userepl", "bool"}, + {"usebypassrls", "bool"}, + {"passwd", "text"}, + {"valuntil", "timestamp"}, + {"useconfig", "text[]"}, + }, + Values: []string{ + "bemidb", + "10", + "FALSE", + "FALSE", + "TRUE", + "FALSE", + "", + "NULL", + "NULL", + }, +} + +var PG_ROLES_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"oid", "oid"}, + {"rolname", "text"}, + {"rolsuper", "bool"}, + {"rolinherit", "bool"}, + {"rolcreaterole", "bool"}, + {"rolcreatedb", "bool"}, + {"rolcanlogin", "bool"}, + {"rolreplication", "bool"}, + {"rolconnlimit", "int4"}, + {"rolpassword", "text"}, + {"rolvaliduntil", "timestamp"}, + {"rolbypassrls", "bool"}, + {"rolconfig", "text[]"}, + }, + Values: []string{ + "10", + "", + "true", + "true", + "true", + "true", + "true", + "false", + "-1", + "NULL", + "NULL", + "false", + "NULL", + }, +} + +var PG_USER_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"usename", "text"}, + {"usesysid", "oid"}, + {"usecreatedb", "bool"}, + {"usesuper", "bool"}, + {"userepl", "bool"}, + {"usebypassrls", "bool"}, + {"passwd", "text"}, + {"valuntil", "timestamp"}, + {"useconfig", "text[]"}, + }, + Values: []string{ + "", + "10", + "t", + "t", + "t", + "t", + "", + "NULL", + "NULL", + }, +} + +var PG_DATABASE_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"oid", "oid"}, + {"datname", "text"}, + {"datdba", "oid"}, + {"encoding", "int4"}, + {"datlocprovider", "text"}, + {"datistemplate", "bool"}, + {"datallowconn", "bool"}, + {"datconnlimit", "int4"}, + {"datfrozenxid", "int8"}, + {"datminmxid", "int4"}, + {"dattablespace", "oid"}, + {"datcollate", "text"}, + {"datctype", "text"}, + {"datlocale", "text"}, + {"daticurules", "text"}, + {"datcollversion", "text"}, + {"datacl", "text[]"}, + }, + Values: []string{ + "16388", + "", + "10", + "6", + "c", + "FALSE", + "TRUE", + "-1", + "722", + "1", + "1663", + "en_US.UTF-8", + "en_US.UTF-8", + "NULL", + "NULL", + "NULL", + "NULL", + }, +} + +var PG_EXTENSION_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"oid", "oid"}, + {"extname", "text"}, + {"extowner", "oid"}, + {"extnamespace", "oid"}, + {"extrelocatable", "bool"}, + {"extversion", "text"}, + {"extconfig", "text[]"}, + {"extcondition", "text[]"}, + }, + Values: []string{ + "13823", + "plpgsql", + "10", + "11", + "false", + "1.0", + "NULL", + "NULL", + }, +} + +var PG_STAT_USER_TABLES_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"relid", "oid"}, + {"schemaname", "text"}, + {"relname", "text"}, + {"seq_scan", "int8"}, + {"last_seq_scan", "timestamp"}, + {"seq_tup_read", "int8"}, + {"idx_scan", "int8"}, + {"last_idx_scan", "timestamp"}, + {"idx_tup_fetch", "int8"}, + {"n_tup_ins", "int8"}, + {"n_tup_upd", "int8"}, + {"n_tup_del", "int8"}, + {"n_tup_hot_upd", "int8"}, + {"n_tup_newpage_upd", "int8"}, + {"n_live_tup", "int8"}, + {"n_dead_tup", "int8"}, + {"n_mod_since_analyze", "int8"}, + {"n_ins_since_vacuum", "int8"}, + {"last_vacuum", "timestamp"}, + {"last_autovacuum", "timestamp"}, + {"last_analyze", "timestamp"}, + {"last_autoanalyze", "timestamp"}, + {"vacuum_count", "int8"}, + {"autovacuum_count", "int8"}, + {"analyze_count", "int8"}, + {"autoanalyze_count", "int8"}, + }, + Values: []string{ + "123456", + "", + "", + "0", + "NULL", + "0", + "0", + "NULL", + "0", + "0", + "0", + "0", + "0", + "0", + "1", + "0", + "0", + "0", + "NULL", + "NULL", + "NULL", + "NULL", + "0", + "0", + "0", + "0", + }, +} + +var PG_STAT_GSSAPI_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"pid", "int4"}, + {"gss_authenticated", "bool"}, + {"principal", "text"}, + {"encrypted", "bool"}, + {"credentials_delegated", "bool"}, + }, +} + +var PG_AUTH_MEMBERS_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"oid", "text"}, + {"roleid", "oid"}, + {"member", "oid"}, + {"grantor", "oid"}, + {"admin_option", "bool"}, + {"inherit_option", "bool"}, + {"set_option", "bool"}, + }, +} + +var PG_STAT_ACTIVITY_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"datid", "oid"}, + {"datname", "text"}, + {"pid", "int4"}, + {"usesysid", "oid"}, + {"usename", "text"}, + {"application_name", "text"}, + {"client_addr", "inet"}, + {"client_hostname", "text"}, + {"client_port", "int4"}, + {"backend_start", "timestamp"}, + {"xact_start", "timestamp"}, + {"query_start", "timestamp"}, + {"state_change", "timestamp"}, + {"wait_event_type", "text"}, + {"wait_event", "text"}, + {"state", "text"}, + {"backend_xid", "int8"}, + {"backend_xmin", "int8"}, + {"query", "text"}, + {"backend_type", "text"}, + }, +} + +var PG_VIEWS_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"schemaname", "text"}, + {"viewname", "text"}, + {"viewowner", "text"}, + {"definition", "text"}, + }, +} + +var PG_MATVIEWS_DEFINITION = TableDefinition{ + Columns: []ColumnDefinition{ + {"schemaname", "text"}, + {"matviewname", "text"}, + {"matviewowner", "text"}, + {"tablespace", "text"}, + {"hasindexes", "bool"}, + {"ispopulated", "bool"}, + {"definition", "text"}, + }, +} + var PG_SYSTEM_TABLES = NewSet([]string{ "pg_aggregate", "pg_am", diff --git a/src/query_handler_test.go b/src/query_handler_test.go index 4287a14..4dd2446 100644 --- a/src/query_handler_test.go +++ b/src/query_handler_test.go @@ -69,8 +69,8 @@ func TestHandleQuery(t *testing.T) { }, "SELECT * from pg_is_in_recovery()": { "description": {"pg_is_in_recovery"}, - "types": {Uint32ToString(pgtype.TextOID)}, - "values": {"f"}, + "types": {Uint32ToString(pgtype.BoolOID)}, + "values": {"false"}, }, "SELECT row_to_json(t) FROM (SELECT usename, passwd FROM pg_shadow WHERE usename='bemidb') t": { "description": {"row_to_json"}, @@ -126,7 +126,7 @@ func TestHandleQuery(t *testing.T) { }, "SELECT slot_name FROM pg_replication_slots": { "description": {"slot_name"}, - "types": {Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.TextOID)}, "values": {}, }, "SELECT oid, datname, datdba FROM pg_catalog.pg_database where oid = 16388": { @@ -136,27 +136,27 @@ func TestHandleQuery(t *testing.T) { }, "SELECT * FROM pg_catalog.pg_stat_gssapi": { "description": {"pid", "gss_authenticated", "principal", "encrypted", "credentials_delegated"}, - "types": {Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.Int4OID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.TextOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID)}, "values": {}, }, "SELECT * FROM pg_catalog.pg_user": { "description": {"usename", "usesysid", "usecreatedb", "usesuper", "userepl", "usebypassrls", "passwd", "valuntil", "useconfig"}, "types": {Uint32ToString(pgtype.TextOID)}, - "values": {"bemidb", "10", "t", "t", "t", "t", "", "", ""}, + "values": {"bemidb", "10", "true", "true", "true", "true", "", "", ""}, }, "SELECT datid FROM pg_catalog.pg_stat_activity": { "description": {"datid"}, - "types": {Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.Int8OID)}, "values": {}, }, "SELECT schemaname, matviewname AS objectname FROM pg_catalog.pg_matviews": { "description": {"schemaname", "objectname"}, - "types": {Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.TextOID), Uint32ToString(pgtype.TextOID)}, "values": {}, }, "SELECT * FROM pg_catalog.pg_views": { "description": {"schemaname", "viewname", "viewowner", "definition"}, - "types": {Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.TextOID), Uint32ToString(pgtype.TextOID), Uint32ToString(pgtype.TextOID), Uint32ToString(pgtype.TextOID)}, }, "SELECT schemaname, relname, n_live_tup FROM pg_stat_user_tables": { "description": {"schemaname", "relname", "n_live_tup"}, @@ -188,7 +188,7 @@ func TestHandleQuery(t *testing.T) { }, "SELECT * FROM pg_catalog.pg_shdescription": { "description": {"objoid", "classoid", "description"}, - "types": {Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.OIDOID), Uint32ToString(pgtype.OIDOID), Uint32ToString(pgtype.TextOID)}, }, "SELECT * FROM pg_catalog.pg_roles": { "description": {"oid", "rolname", "rolsuper", "rolinherit", "rolcreaterole", "rolcreatedb", "rolcanlogin", "rolreplication", "rolconnlimit", "rolpassword", "rolvaliduntil", "rolbypassrls", "rolconfig"}, @@ -201,7 +201,7 @@ func TestHandleQuery(t *testing.T) { Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), - Uint32ToString(pgtype.Int8OID), + Uint32ToString(pgtype.Int4OID), Uint32ToString(pgtype.Int4OID), Uint32ToString(pgtype.Int4OID), Uint32ToString(pgtype.BoolOID), @@ -706,7 +706,7 @@ func TestHandleQuery(t *testing.T) { }, "SELECT a.oid, pd.description FROM pg_catalog.pg_roles a LEFT JOIN pg_catalog.pg_shdescription pd ON a.oid = pd.objoid": { "description": {"oid", "description"}, - "types": {Uint32ToString(pgtype.OIDOID), Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.OIDOID), Uint32ToString(pgtype.TextOID)}, "values": {"10", ""}, }, @@ -784,32 +784,32 @@ func TestHandleQuery(t *testing.T) { }, "SELECT pg_inherits.inhrelid FROM pg_inherits": { "description": {"inhrelid"}, - "types": {Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.Int8OID)}, "values": {}, }, "SELECT pg_shdescription.objoid FROM pg_shdescription": { "description": {"objoid"}, - "types": {Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.OIDOID)}, "values": {}, }, "SELECT pg_statio_user_tables.relid FROM pg_statio_user_tables": { "description": {"relid"}, - "types": {Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.Int8OID)}, "values": {}, }, "SELECT pg_replication_slots.slot_name FROM pg_replication_slots": { "description": {"slot_name"}, - "types": {Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.TextOID)}, "values": {}, }, "SELECT pg_stat_gssapi.pid FROM pg_stat_gssapi": { "description": {"pid"}, - "types": {Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.Int4OID)}, "values": {}, }, "SELECT pg_auth_members.oid FROM pg_auth_members": { "description": {"oid"}, - "types": {Uint32ToString(pgtype.BoolOID)}, + "types": {Uint32ToString(pgtype.TextOID)}, "values": {}, }, "SELECT tables.table_name FROM information_schema.tables": { @@ -836,7 +836,7 @@ func TestHandleQuery(t *testing.T) { Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.BoolOID), Uint32ToString(pgtype.Int8OID), - Uint32ToString(pgtype.BoolOID), + Uint32ToString(pgtype.TextOID), }, "values": {"16388", "bemidb", "", "true", "false", "true", "10", ""}, }, @@ -913,7 +913,7 @@ func TestHandleParseQuery(t *testing.T) { &pgproto3.ParseComplete{}, }) - remappedQuery := "SELECT usename, passwd FROM (VALUES ('bemidb', '10'::int8, 'FALSE'::bool, 'FALSE'::bool, 'TRUE'::bool, 'FALSE'::bool, 'bemidb-encrypted', NULL, NULL)) pg_shadow(usename, usesysid, usecreatedb, usesuper, userepl, usebypassrls, passwd, valuntil, useconfig) WHERE usename = $1" + remappedQuery := "SELECT usename, passwd FROM (VALUES ('bemidb'::text, '10'::oid, 'FALSE'::bool, 'FALSE'::bool, 'TRUE'::bool, 'FALSE'::bool, 'bemidb-encrypted'::text, NULL, NULL)) pg_shadow(usename, usesysid, usecreatedb, usesuper, userepl, usebypassrls, passwd, valuntil, useconfig) WHERE usename = $1" if preparedStatement.Query != remappedQuery { t.Errorf("Expected the prepared statement query to be %v, got %v", remappedQuery, preparedStatement.Query) } diff --git a/src/query_remapper_table.go b/src/query_remapper_table.go index 2959508..d1046d3 100644 --- a/src/query_remapper_table.go +++ b/src/query_remapper_table.go @@ -53,15 +53,15 @@ func (remapper *QueryRemapperTable) RemapTable(node *pgQuery.Node) *pgQuery.Node // pg_catalog.pg_inherits -> return nothing case PG_TABLE_PG_INHERITS: - return parser.MakeEmptyTableNode(PG_TABLE_PG_INHERITS, PG_INHERITS_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_INHERITS, PG_INHERITS_DEFINITION, qSchemaTable.Alias) // pg_catalog.pg_shdescription -> return nothing case PG_TABLE_PG_SHDESCRIPTION: - return parser.MakeEmptyTableNode(PG_TABLE_PG_SHDESCRIPTION, PG_SHDESCRIPTION_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_SHDESCRIPTION, PG_SHDESCRIPTION_DEFINITION, qSchemaTable.Alias) // pg_catalog.pg_statio_user_tables -> return nothing case PG_TABLE_PG_STATIO_USER_TABLES: - return parser.MakeEmptyTableNode(PG_TABLE_PG_STATIO_USER_TABLES, PG_STATIO_USER_TABLES_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_STATIO_USER_TABLES, PG_STATIO_USER_TABLES_DEFINITION, qSchemaTable.Alias) // pg_catalog.pg_extension -> return hard-coded extension info case PG_TABLE_PG_EXTENSION: @@ -69,7 +69,7 @@ func (remapper *QueryRemapperTable) RemapTable(node *pgQuery.Node) *pgQuery.Node // pg_replication_slots -> return nothing case PG_TABLE_PG_REPLICATION_SLOTS: - return parser.MakeEmptyTableNode(PG_TABLE_PG_REPLICATION_SLOTS, PG_REPLICATION_SLOTS_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_REPLICATION_SLOTS, PG_REPLICATION_SLOTS_DEFINITION, qSchemaTable.Alias) // pg_catalog.pg_database -> return hard-coded database info case PG_TABLE_PG_DATABASE: @@ -77,11 +77,11 @@ func (remapper *QueryRemapperTable) RemapTable(node *pgQuery.Node) *pgQuery.Node // pg_catalog.pg_stat_gssapi -> return nothing case PG_TABLE_PG_STAT_GSSAPI: - return parser.MakeEmptyTableNode(PG_TABLE_PG_STAT_GSSAPI, PG_STAT_GSSAPI_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_STAT_GSSAPI, PG_STAT_GSSAPI_DEFINITION, qSchemaTable.Alias) // pg_catalog.pg_auth_members -> return empty table case PG_TABLE_PG_AUTH_MEMBERS: - return parser.MakeEmptyTableNode(PG_TABLE_PG_AUTH_MEMBERS, PG_AUTH_MEMBERS_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_AUTH_MEMBERS, PG_AUTH_MEMBERS_DEFINITION, qSchemaTable.Alias) // pg_catalog.pg_user -> return hard-coded user info case PG_TABLE_PG_USER: @@ -89,15 +89,15 @@ func (remapper *QueryRemapperTable) RemapTable(node *pgQuery.Node) *pgQuery.Node // pg_stat_activity -> return empty table case PG_TABLE_PG_STAT_ACTIVITY: - return parser.MakeEmptyTableNode(PG_TABLE_PG_STAT_ACTIVITY, PG_STAT_ACTIVITY_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_STAT_ACTIVITY, PG_STAT_ACTIVITY_DEFINITION, qSchemaTable.Alias) // pg_views -> return empty table case PG_TABLE_PG_VIEWS: - return parser.MakeEmptyTableNode(PG_TABLE_PG_VIEWS, PG_VIEWS_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_VIEWS, PG_VIEWS_DEFINITION, qSchemaTable.Alias) // pg_matviews -> return empty table case PG_TABLE_PG_MATVIEWS: - return parser.MakeEmptyTableNode(PG_TABLE_PG_MATVIEWS, PG_MATVIEWS_COLUMNS, qSchemaTable.Alias) + return parser.MakeEmptyTableNode(PG_TABLE_PG_MATVIEWS, PG_MATVIEWS_DEFINITION, qSchemaTable.Alias) // pg_stat_user_tables -> return hard-coded table info case PG_TABLE_PG_STAT_USER_TABLES: @@ -231,107 +231,3 @@ func (remapper *QueryRemapperTable) isFunctionFromPgCatalog(schemaFunction PgSch return schemaFunction.Schema == PG_SCHEMA_PG_CATALOG || (schemaFunction.Schema == "" && PG_SYSTEM_FUNCTIONS.Contains(schemaFunction.Function)) } - -var PG_INHERITS_COLUMNS = []string{ - "inhrelid", - "inhparent", - "inhseqno", - "inhdetachpending", -} - -var PG_SHDESCRIPTION_COLUMNS = []string{ - "objoid", - "classoid", - "description", -} - -var PG_STATIO_USER_TABLES_COLUMNS = []string{ - "relid", - "schemaname", - "relname", - "heap_blks_read", - "heap_blks_hit", - "idx_blks_read", - "idx_blks_hit", - "toast_blks_read", - "toast_blks_hit", - "tidx_blks_read", - "tidx_blks_hit", -} - -var PG_REPLICATION_SLOTS_COLUMNS = []string{ - "slot_name", - "plugin", - "slot_type", - "datoid", - "database", - "temporary", - "active", - "active_pid", - "xmin", - "catalog_xmin", - "restart_lsn", - "confirmed_flush_lsn", - "wal_status", - "safe_wal_size", - "two_phase", - "conflicting", -} - -var PG_STAT_GSSAPI_COLUMNS = []string{ - "pid", - "gss_authenticated", - "principal", - "encrypted", - "credentials_delegated", -} - -var PG_AUTH_MEMBERS_COLUMNS = []string{ - "oid", - "roleid", - "member", - "grantor", - "admin_option", - "inherit_option", - "set_option", -} - -var PG_STAT_ACTIVITY_COLUMNS = []string{ - "datid", - "datname", - "pid", - "usesysid", - "usename", - "application_name", - "client_addr", - "client_hostname", - "client_port", - "backend_start", - "xact_start", - "query_start", - "state_change", - "wait_event_type", - "wait_event", - "state", - "backend_xid", - "backend_xmin", - "query", - "backend_type", -} - -var PG_VIEWS_COLUMNS = []string{ - "schemaname", - "viewname", - "viewowner", - "definition", -} - -var PG_MATVIEWS_COLUMNS = []string{ - "schemaname", - "matviewname", - "matviewowner", - "tablespace", - "hasindexes", - "ispopulated", - "definition", -}