diff --git a/src/main.go b/src/main.go index 28ea051..f09eccb 100644 --- a/src/main.go +++ b/src/main.go @@ -6,7 +6,7 @@ import ( "time" ) -const VERSION = "0.25.0" +const VERSION = "0.25.1" func main() { config := LoadConfig() diff --git a/src/query_handler_test.go b/src/query_handler_test.go index f5729b6..7204fd8 100644 --- a/src/query_handler_test.go +++ b/src/query_handler_test.go @@ -24,6 +24,10 @@ func TestHandleQuery(t *testing.T) { "description": {"quote_ident"}, "values": {"\"fooBar\""}, }, + "SELECT setting from pg_show_all_settings() WHERE name = 'default_null_order'": { + "description": {"setting"}, + "values": {"nulls_last"}, + }, // pg_get_partkeydef "SELECT pg_catalog.pg_get_partkeydef(c.oid) FROM pg_catalog.pg_class c LIMIT 1": { "description": {"pg_get_partkeydef"}, diff --git a/src/query_parser_table.go b/src/query_parser_table.go index 1f4ba61..9b1b13b 100644 --- a/src/query_parser_table.go +++ b/src/query_parser_table.go @@ -16,8 +16,9 @@ const ( PG_SCHEMA_INFORMATION_SCHEMA = "information_schema" PG_TABLE_TABLES = "tables" - PG_FUNCTION_PG_GET_KEYWORDS = "pg_get_keywords" - PG_FUNCTION_ARRAY_UPPER = "array_upper" + PG_FUNCTION_PG_GET_KEYWORDS = "pg_get_keywords" + PG_FUNCTION_ARRAY_UPPER = "array_upper" + PG_FUNCTION_PG_SHOW_ALL_SETTINGS = "pg_show_all_settings" ) type QueryParserTable struct { @@ -267,6 +268,128 @@ func (parser *QueryParserTable) MakeArrayUpperNode(funcCallNode *pgQuery.FuncCal ).GetFuncCall() } +// pg_show_all_settings() +func (parser *QueryParserTable) IsPgShowAllSettingsFunction(node *pgQuery.Node) bool { + for _, funcNode := range node.GetRangeFunction().Functions { + for _, funcItemNode := range funcNode.GetList().Items { + funcCallNode := funcItemNode.GetFuncCall() + if funcCallNode == nil { + continue + } + if len(funcCallNode.Funcname) != 1 { + continue + } + + function := funcCallNode.Funcname[0].GetString_().Sval + if function == PG_FUNCTION_PG_SHOW_ALL_SETTINGS { + return true + } + } + } + + return false +} + +// pg_show_all_settings() -> duckdb_settings() mapped to pg format +func (parser *QueryParserTable) MakePgShowAllSettingsNode(node *pgQuery.Node) *pgQuery.Node { + return &pgQuery.Node{ + Node: &pgQuery.Node_RangeSubselect{ + RangeSubselect: &pgQuery.RangeSubselect{ + Subquery: &pgQuery.Node{ + Node: &pgQuery.Node_SelectStmt{ + SelectStmt: &pgQuery.SelectStmt{ + TargetList: []*pgQuery.Node{ + parser.utils.MakeResTargetNode( + pgQuery.MakeColumnRefNode([]*pgQuery.Node{pgQuery.MakeStrNode("name")}, 0), + "name", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeColumnRefNode([]*pgQuery.Node{pgQuery.MakeStrNode("value")}, 0), + "setting", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("", 0), + "unit", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("Settings", 0), + "category", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeColumnRefNode([]*pgQuery.Node{pgQuery.MakeStrNode("description")}, 0), + "short_desc", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("", 0), + "extra_desc", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeColumnRefNode([]*pgQuery.Node{pgQuery.MakeStrNode("scope")}, 0), + "context", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeColumnRefNode([]*pgQuery.Node{pgQuery.MakeStrNode("input_type")}, 0), + "vartype", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("default", 0), + "source", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("", 0), + "min_val", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("", 0), + "max_val", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("", 0), + "enumvals", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeColumnRefNode([]*pgQuery.Node{pgQuery.MakeStrNode("value")}, 0), + "boot_val", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeColumnRefNode([]*pgQuery.Node{pgQuery.MakeStrNode("value")}, 0), + "reset_val", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("", 0), + "sourcefile", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("0", 0), + "sourceline", + ), + parser.utils.MakeResTargetNode( + pgQuery.MakeAConstStrNode("f", 0), + "pending_restart", + ), + }, + FromClause: []*pgQuery.Node{ + pgQuery.MakeSimpleRangeFunctionNode([]*pgQuery.Node{ + pgQuery.MakeListNode([]*pgQuery.Node{ + pgQuery.MakeFuncCallNode( + []*pgQuery.Node{ + pgQuery.MakeStrNode("duckdb_settings"), + }, + nil, + 0, + ), + }), + }), + }, + }, + }, + }, + Alias: node.GetAlias(), + }, + }, + } +} + func (parser *QueryParserTable) isPgCatalogSchema(qSchemaTable QuerySchemaTable) bool { return qSchemaTable.Schema == PG_SCHEMA_PG_CATALOG || qSchemaTable.Schema == "" } diff --git a/src/query_parser_utils.go b/src/query_parser_utils.go index 147121c..03570ef 100644 --- a/src/query_parser_utils.go +++ b/src/query_parser_utils.go @@ -67,3 +67,14 @@ func (utils *QueryParserUtils) MakeAConstBoolNode(val bool) *pgQuery.Node { }, } } + +func (utils *QueryParserUtils) MakeResTargetNode(val *pgQuery.Node, name string) *pgQuery.Node { + return &pgQuery.Node{ + Node: &pgQuery.Node_ResTarget{ + ResTarget: &pgQuery.ResTarget{ + Name: name, + Val: val, + }, + }, + } +} diff --git a/src/select_remapper_table.go b/src/select_remapper_table.go index 34867e2..5d68e76 100644 --- a/src/select_remapper_table.go +++ b/src/select_remapper_table.go @@ -103,6 +103,11 @@ func (remapper *SelectRemapperTable) RemapTableFunction(node *pgQuery.Node) *pgQ return remapper.parserTable.MakePgGetKeywordsNode(node) } + // pg_show_all_settings() -> duckdb_settings() + if remapper.parserTable.IsPgShowAllSettingsFunction(node) { + return remapper.parserTable.MakePgShowAllSettingsNode(node) + } + return node }