diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f0f89b7..a197255d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ * ... +## 6.1.0 2025-02-24 + +* Upgrade to libpg_query 17-6.1.0 + - Update to Postgres 17.4, and add recent patches scheduled for Postgres 17.5 (not yet released) + - Notably, this pulls in support for macOS 15.4 which defines strchrnul + in its standard library, fixing builds on up-to-date macOS versions. + - Deparser improvements + - Add parenthesis around AT LOCAL / AT TIMEZONE if needed + - Correctness improvements related to expressions and function calls +* Allow vendoring pg_query_go with built-in "go mod vendor" command [(#131)](https://github.com/pganalyze/pg_query_go/pull/131) + + ## 6.0.0 2024-11-26 * Upgrade to libpg_query 17-6.0.0 diff --git a/Makefile b/Makefile index 524d39a8..04dcf7fa 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ benchmark: # --- Below only needed for releasing new versions -LIB_PG_QUERY_TAG = 17-6.0.0 +LIB_PG_QUERY_TAG = 17-6.1.0 root_dir := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) LIB_TMPDIR = $(root_dir)/tmp diff --git a/parse_test.go b/parse_test.go index 4e628951..32e929b0 100644 --- a/parse_test.go +++ b/parse_test.go @@ -22,9 +22,9 @@ var parseTests = []struct { }{ { "SELECT 1", - `{"version":170000,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"A_Const":{"ival":{"ival":1},"location":7}},"location":7}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, + `{"version":170004,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"A_Const":{"ival":{"ival":1},"location":7}},"location":7}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ @@ -47,9 +47,9 @@ var parseTests = []struct { }, { "SELECT * FROM x WHERE z = 1", - `{"version":170000,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"ColumnRef":{"fields":[{"A_Star":{}}],"location":7}},"location":7}}],"fromClause":[{"RangeVar":{"relname":"x","inh":true,"relpersistence":"p","location":14}}],"whereClause":{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"="}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"z"}}],"location":22}},"rexpr":{"A_Const":{"ival":{"ival":1},"location":26}},"location":24}},"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, + `{"version":170004,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"ColumnRef":{"fields":[{"A_Star":{}}],"location":7}},"location":7}}],"fromClause":[{"RangeVar":{"relname":"x","inh":true,"relpersistence":"p","location":14}}],"whereClause":{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"="}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"z"}}],"location":22}},"rexpr":{"A_Const":{"ival":{"ival":1},"location":26}},"location":24}},"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ @@ -98,9 +98,9 @@ var parseTests = []struct { }, { `INSERT INTO "schema_index_stats" ("snapshot_id","schema_index_id","size_bytes") VALUES (11710849,8448632,16384),(11710849,8448633,16384) RETURNING id`, - `{"version":170000,"stmts":[{"stmt":{"InsertStmt":{"relation":{"relname":"schema_index_stats","inh":true,"relpersistence":"p","location":12},"cols":[{"ResTarget":{"name":"snapshot_id","location":34}},{"ResTarget":{"name":"schema_index_id","location":48}},{"ResTarget":{"name":"size_bytes","location":66}}],"selectStmt":{"SelectStmt":{"valuesLists":[{"List":{"items":[{"A_Const":{"ival":{"ival":11710849},"location":88}},{"A_Const":{"ival":{"ival":8448632},"location":97}},{"A_Const":{"ival":{"ival":16384},"location":105}}]}},{"List":{"items":[{"A_Const":{"ival":{"ival":11710849},"location":113}},{"A_Const":{"ival":{"ival":8448633},"location":122}},{"A_Const":{"ival":{"ival":16384},"location":130}}]}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}},"returningList":[{"ResTarget":{"val":{"ColumnRef":{"fields":[{"String":{"sval":"id"}}],"location":147}},"location":147}}],"override":"OVERRIDING_NOT_SET"}}}]}`, + `{"version":170004,"stmts":[{"stmt":{"InsertStmt":{"relation":{"relname":"schema_index_stats","inh":true,"relpersistence":"p","location":12},"cols":[{"ResTarget":{"name":"snapshot_id","location":34}},{"ResTarget":{"name":"schema_index_id","location":48}},{"ResTarget":{"name":"size_bytes","location":66}}],"selectStmt":{"SelectStmt":{"valuesLists":[{"List":{"items":[{"A_Const":{"ival":{"ival":11710849},"location":88}},{"A_Const":{"ival":{"ival":8448632},"location":97}},{"A_Const":{"ival":{"ival":16384},"location":105}}]}},{"List":{"items":[{"A_Const":{"ival":{"ival":11710849},"location":113}},{"A_Const":{"ival":{"ival":8448633},"location":122}},{"A_Const":{"ival":{"ival":16384},"location":130}}]}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}},"returningList":[{"ResTarget":{"val":{"ColumnRef":{"fields":[{"String":{"sval":"id"}}],"location":147}},"location":147}}],"override":"OVERRIDING_NOT_SET"}}}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ @@ -153,9 +153,9 @@ var parseTests = []struct { }, { "SELECT * FROM x WHERE y IN ($1)", - `{"version":170000,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"ColumnRef":{"fields":[{"A_Star":{}}],"location":7}},"location":7}}],"fromClause":[{"RangeVar":{"relname":"x","inh":true,"relpersistence":"p","location":14}}],"whereClause":{"A_Expr":{"kind":"AEXPR_IN","name":[{"String":{"sval":"="}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"y"}}],"location":22}},"rexpr":{"List":{"items":[{"ParamRef":{"number":1,"location":28}}]}},"location":24}},"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, + `{"version":170004,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"ColumnRef":{"fields":[{"A_Star":{}}],"location":7}},"location":7}}],"fromClause":[{"RangeVar":{"relname":"x","inh":true,"relpersistence":"p","location":14}}],"whereClause":{"A_Expr":{"kind":"AEXPR_IN","name":[{"String":{"sval":"="}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"y"}}],"location":22}},"rexpr":{"List":{"items":[{"ParamRef":{"number":1,"location":28}}]}},"location":24}},"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ @@ -217,9 +217,9 @@ var parseTests = []struct { AND n.nspname !~ '^pg_toast' AND pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1,2;`, - `{"version":170000,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"name":"Schema","val":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"nspname"}}],"location":7}},"location":7}},{"ResTarget":{"name":"Name","val":{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relname"}}],"location":36}},"location":36}},{"ResTarget":{"name":"Type","val":{"CaseExpr":{"arg":{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relkind"}}],"location":68}},"args":[{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"r"},"location":83}},"result":{"A_Const":{"sval":{"sval":"table"},"location":92}},"location":78}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"v"},"location":105}},"result":{"A_Const":{"sval":{"sval":"view"},"location":114}},"location":100}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"m"},"location":126}},"result":{"A_Const":{"sval":{"sval":"materialized view"},"location":135}},"location":121}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"i"},"location":160}},"result":{"A_Const":{"sval":{"sval":"index"},"location":169}},"location":155}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"S"},"location":182}},"result":{"A_Const":{"sval":{"sval":"sequence"},"location":191}},"location":177}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"s"},"location":207}},"result":{"A_Const":{"sval":{"sval":"special"},"location":216}},"location":202}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"f"},"location":231}},"result":{"A_Const":{"sval":{"sval":"foreign table"},"location":240}},"location":226}}],"location":63}},"location":63}},{"ResTarget":{"name":"Owner","val":{"FuncCall":{"funcname":[{"String":{"sval":"pg_catalog"}},{"String":{"sval":"pg_get_userbyid"}}],"args":[{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relowner"}}],"location":304}}],"funcformat":"COERCE_EXPLICIT_CALL","location":277}},"location":277}}],"fromClause":[{"JoinExpr":{"jointype":"JOIN_LEFT","larg":{"RangeVar":{"schemaname":"pg_catalog","relname":"pg_class","inh":true,"relpersistence":"p","alias":{"aliasname":"c"},"location":336}},"rarg":{"RangeVar":{"schemaname":"pg_catalog","relname":"pg_namespace","inh":true,"relpersistence":"p","alias":{"aliasname":"n"},"location":374}},"quals":{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"="}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"oid"}}],"location":403}},"rexpr":{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relnamespace"}}],"location":411}},"location":409}}}}],"whereClause":{"BoolExpr":{"boolop":"AND_EXPR","args":[{"A_Expr":{"kind":"AEXPR_IN","name":[{"String":{"sval":"="}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relkind"}}],"location":435}},"rexpr":{"List":{"items":[{"A_Const":{"sval":{"sval":"r"},"location":449}},{"A_Const":{"sval":{"sval":""},"location":453}}]}},"location":445}},{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"\u003c\u003e"}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"nspname"}}],"location":467}},"rexpr":{"A_Const":{"sval":{"sval":"pg_catalog"},"location":480}},"location":477}},{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"\u003c\u003e"}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"nspname"}}],"location":503}},"rexpr":{"A_Const":{"sval":{"sval":"information_schema"},"location":516}},"location":513}},{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"!~"}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"nspname"}}],"location":547}},"rexpr":{"A_Const":{"sval":{"sval":"^pg_toast"},"location":560}},"location":557}},{"FuncCall":{"funcname":[{"String":{"sval":"pg_catalog"}},{"String":{"sval":"pg_table_is_visible"}}],"args":[{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"oid"}}],"location":613}}],"funcformat":"COERCE_EXPLICIT_CALL","location":582}}],"location":463}},"sortClause":[{"SortBy":{"node":{"A_Const":{"ival":{"ival":1},"location":632}},"sortby_dir":"SORTBY_DEFAULT","sortby_nulls":"SORTBY_NULLS_DEFAULT","location":-1}},{"SortBy":{"node":{"A_Const":{"ival":{"ival":2},"location":634}},"sortby_dir":"SORTBY_DEFAULT","sortby_nulls":"SORTBY_NULLS_DEFAULT","location":-1}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}},"stmt_len":635}]}`, + `{"version":170004,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"name":"Schema","val":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"nspname"}}],"location":7}},"location":7}},{"ResTarget":{"name":"Name","val":{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relname"}}],"location":36}},"location":36}},{"ResTarget":{"name":"Type","val":{"CaseExpr":{"arg":{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relkind"}}],"location":68}},"args":[{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"r"},"location":83}},"result":{"A_Const":{"sval":{"sval":"table"},"location":92}},"location":78}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"v"},"location":105}},"result":{"A_Const":{"sval":{"sval":"view"},"location":114}},"location":100}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"m"},"location":126}},"result":{"A_Const":{"sval":{"sval":"materialized view"},"location":135}},"location":121}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"i"},"location":160}},"result":{"A_Const":{"sval":{"sval":"index"},"location":169}},"location":155}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"S"},"location":182}},"result":{"A_Const":{"sval":{"sval":"sequence"},"location":191}},"location":177}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"s"},"location":207}},"result":{"A_Const":{"sval":{"sval":"special"},"location":216}},"location":202}},{"CaseWhen":{"expr":{"A_Const":{"sval":{"sval":"f"},"location":231}},"result":{"A_Const":{"sval":{"sval":"foreign table"},"location":240}},"location":226}}],"location":63}},"location":63}},{"ResTarget":{"name":"Owner","val":{"FuncCall":{"funcname":[{"String":{"sval":"pg_catalog"}},{"String":{"sval":"pg_get_userbyid"}}],"args":[{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relowner"}}],"location":304}}],"funcformat":"COERCE_EXPLICIT_CALL","location":277}},"location":277}}],"fromClause":[{"JoinExpr":{"jointype":"JOIN_LEFT","larg":{"RangeVar":{"schemaname":"pg_catalog","relname":"pg_class","inh":true,"relpersistence":"p","alias":{"aliasname":"c"},"location":336}},"rarg":{"RangeVar":{"schemaname":"pg_catalog","relname":"pg_namespace","inh":true,"relpersistence":"p","alias":{"aliasname":"n"},"location":374}},"quals":{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"="}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"oid"}}],"location":403}},"rexpr":{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relnamespace"}}],"location":411}},"location":409}}}}],"whereClause":{"BoolExpr":{"boolop":"AND_EXPR","args":[{"A_Expr":{"kind":"AEXPR_IN","name":[{"String":{"sval":"="}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"relkind"}}],"location":435}},"rexpr":{"List":{"items":[{"A_Const":{"sval":{"sval":"r"},"location":449}},{"A_Const":{"sval":{"sval":""},"location":453}}]}},"location":445}},{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"\u003c\u003e"}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"nspname"}}],"location":467}},"rexpr":{"A_Const":{"sval":{"sval":"pg_catalog"},"location":480}},"location":477}},{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"\u003c\u003e"}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"nspname"}}],"location":503}},"rexpr":{"A_Const":{"sval":{"sval":"information_schema"},"location":516}},"location":513}},{"A_Expr":{"kind":"AEXPR_OP","name":[{"String":{"sval":"!~"}}],"lexpr":{"ColumnRef":{"fields":[{"String":{"sval":"n"}},{"String":{"sval":"nspname"}}],"location":547}},"rexpr":{"A_Const":{"sval":{"sval":"^pg_toast"},"location":560}},"location":557}},{"FuncCall":{"funcname":[{"String":{"sval":"pg_catalog"}},{"String":{"sval":"pg_table_is_visible"}}],"args":[{"ColumnRef":{"fields":[{"String":{"sval":"c"}},{"String":{"sval":"oid"}}],"location":613}}],"funcformat":"COERCE_EXPLICIT_CALL","location":582}}],"location":463}},"sortClause":[{"SortBy":{"node":{"A_Const":{"ival":{"ival":1},"location":632}},"sortby_dir":"SORTBY_DEFAULT","sortby_nulls":"SORTBY_NULLS_DEFAULT","location":-1}},{"SortBy":{"node":{"A_Const":{"ival":{"ival":2},"location":634}},"sortby_dir":"SORTBY_DEFAULT","sortby_nulls":"SORTBY_NULLS_DEFAULT","location":-1}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}},"stmt_len":635}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ @@ -399,9 +399,9 @@ var parseTests = []struct { PERFORM 'dummy'; END; $$;`, - `{"version":170000,"stmts":[{"stmt":{"CreateFunctionStmt":{"funcname":[{"String":{"sval":"change_trigger_v2"}}],"returnType":{"names":[{"String":{"sval":"trigger"}}],"typemod":-1,"location":44},"options":[{"DefElem":{"defname":"language","arg":{"String":{"sval":"plpgsql"}},"defaction":"DEFELEM_UNSPEC","location":53}},{"DefElem":{"defname":"as","arg":{"List":{"items":[{"String":{"sval":"\n\t\tDECLARE\n\t\tBEGIN\n\t\t\tPERFORM 'dummy';\n\t\tEND;\n\t\t"}}]}},"defaction":"DEFELEM_UNSPEC","location":71}}]}},"stmt_len":126}]}`, + `{"version":170004,"stmts":[{"stmt":{"CreateFunctionStmt":{"funcname":[{"String":{"sval":"change_trigger_v2"}}],"returnType":{"names":[{"String":{"sval":"trigger"}}],"typemod":-1,"location":44},"options":[{"DefElem":{"defname":"language","arg":{"String":{"sval":"plpgsql"}},"defaction":"DEFELEM_UNSPEC","location":53}},{"DefElem":{"defname":"as","arg":{"List":{"items":[{"String":{"sval":"\n\t\tDECLARE\n\t\tBEGIN\n\t\t\tPERFORM 'dummy';\n\t\tEND;\n\t\t"}}]}},"defaction":"DEFELEM_UNSPEC","location":71}}]}},"stmt_len":126}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ @@ -442,9 +442,9 @@ var parseTests = []struct { id SERIAL PRIMARY KEY, user_id integer DEFAULT 0 NOT NULL, created_at timestamp without time zone NOT NULL);`, - `{"version":170000,"stmts":[{"stmt":{"CreateStmt":{"relation":{"relname":"test","inh":true,"relpersistence":"p","location":13},"tableElts":[{"ColumnDef":{"colname":"id","typeName":{"names":[{"String":{"sval":"serial"}}],"typemod":-1,"location":27},"is_local":true,"constraints":[{"Constraint":{"contype":"CONSTR_PRIMARY","location":34}}],"location":24}},{"ColumnDef":{"colname":"user_id","typeName":{"names":[{"String":{"sval":"pg_catalog"}},{"String":{"sval":"int4"}}],"typemod":-1,"location":59},"is_local":true,"constraints":[{"Constraint":{"contype":"CONSTR_DEFAULT","raw_expr":{"A_Const":{"ival":{},"location":75}},"location":67}},{"Constraint":{"contype":"CONSTR_NOTNULL","location":77}}],"location":51}},{"ColumnDef":{"colname":"created_at","typeName":{"names":[{"String":{"sval":"pg_catalog"}},{"String":{"sval":"timestamp"}}],"typemod":-1,"location":102},"is_local":true,"constraints":[{"Constraint":{"contype":"CONSTR_NOTNULL","location":130}}],"location":91}}],"oncommit":"ONCOMMIT_NOOP"}},"stmt_len":139}]}`, + `{"version":170004,"stmts":[{"stmt":{"CreateStmt":{"relation":{"relname":"test","inh":true,"relpersistence":"p","location":13},"tableElts":[{"ColumnDef":{"colname":"id","typeName":{"names":[{"String":{"sval":"serial"}}],"typemod":-1,"location":27},"is_local":true,"constraints":[{"Constraint":{"contype":"CONSTR_PRIMARY","location":34}}],"location":24}},{"ColumnDef":{"colname":"user_id","typeName":{"names":[{"String":{"sval":"pg_catalog"}},{"String":{"sval":"int4"}}],"typemod":-1,"location":59},"is_local":true,"constraints":[{"Constraint":{"contype":"CONSTR_DEFAULT","raw_expr":{"A_Const":{"ival":{},"location":75}},"location":67}},{"Constraint":{"contype":"CONSTR_NOTNULL","location":77}}],"location":51}},{"ColumnDef":{"colname":"created_at","typeName":{"names":[{"String":{"sval":"pg_catalog"}},{"String":{"sval":"timestamp"}}],"typemod":-1,"location":102},"is_local":true,"constraints":[{"Constraint":{"contype":"CONSTR_NOTNULL","location":130}}],"location":91}}],"oncommit":"ONCOMMIT_NOOP"}},"stmt_len":139}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ @@ -509,9 +509,9 @@ var parseTests = []struct { }, { `SELECT * FROM a(1)`, - `{"version":170000,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"ColumnRef":{"fields":[{"A_Star":{}}],"location":7}},"location":7}}],"fromClause":[{"RangeFunction":{"functions":[{"List":{"items":[{"FuncCall":{"funcname":[{"String":{"sval":"a"}}],"args":[{"A_Const":{"ival":{"ival":1},"location":16}}],"funcformat":"COERCE_EXPLICIT_CALL","location":14}},{}]}}]}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, + `{"version":170004,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"ColumnRef":{"fields":[{"A_Star":{}}],"location":7}},"location":7}}],"fromClause":[{"RangeFunction":{"functions":[{"List":{"items":[{"FuncCall":{"funcname":[{"String":{"sval":"a"}}],"args":[{"A_Const":{"ival":{"ival":1},"location":16}}],"funcformat":"COERCE_EXPLICIT_CALL","location":14}},{}]}}]}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ @@ -552,9 +552,9 @@ var parseTests = []struct { { // Test for null-byte related crashes string([]byte{'S', 'E', 'L', 'E', 'C', 'T', ' ', '1', '\x00'}), - `{"version":170000,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"A_Const":{"ival":{"ival":1},"location":7}},"location":7}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, + `{"version":170004,"stmts":[{"stmt":{"SelectStmt":{"targetList":[{"ResTarget":{"val":{"A_Const":{"ival":{"ival":1},"location":7}},"location":7}}],"limitOption":"LIMIT_OPTION_DEFAULT","op":"SETOP_NONE"}}}]}`, &pg_query.ParseResult{ - Version: int32(170000), + Version: int32(170004), Stmts: []*pg_query.RawStmt{ { Stmt: &pg_query.Node{ diff --git a/parser/include/pg_query.h b/parser/include/pg_query.h index c859fff8..e8353fe3 100644 --- a/parser/include/pg_query.h +++ b/parser/include/pg_query.h @@ -133,8 +133,8 @@ void pg_query_exit(void); // Postgres version information #define PG_MAJORVERSION "17" -#define PG_VERSION "17.0" -#define PG_VERSION_NUM 170000 +#define PG_VERSION "17.4" +#define PG_VERSION_NUM 170004 // Deprecated APIs below diff --git a/parser/include/postgres/access/amapi.h b/parser/include/postgres/access/amapi.h index f25c9d58..d3e5e5d5 100644 --- a/parser/include/postgres/access/amapi.h +++ b/parser/include/postgres/access/amapi.h @@ -76,6 +76,10 @@ typedef enum IndexAMProperty * opfamily. This allows ALTER OPERATOR FAMILY DROP, and causes that to * happen automatically if the operator or support func is dropped. This * is the right behavior for inessential ("loose") objects. + * + * We also make dependencies on lefttype/righttype, of the same strength as + * the dependency on the operator or support func, unless these dependencies + * are redundant with the dependency on the operator or support func. */ typedef struct OpFamilyMember { diff --git a/parser/include/postgres/access/genam.h b/parser/include/postgres/access/genam.h index fdcfbe8d..c25f5d11 100644 --- a/parser/include/postgres/access/genam.h +++ b/parser/include/postgres/access/genam.h @@ -233,5 +233,14 @@ extern SysScanDesc systable_beginscan_ordered(Relation heapRelation, extern HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction); extern void systable_endscan_ordered(SysScanDesc sysscan); +extern void systable_inplace_update_begin(Relation relation, + Oid indexId, + bool indexOK, + Snapshot snapshot, + int nkeys, const ScanKeyData *key, + HeapTuple *oldtupcopy, + void **state); +extern void systable_inplace_update_finish(void *state, HeapTuple tuple); +extern void systable_inplace_update_cancel(void *state); #endif /* GENAM_H */ diff --git a/parser/include/postgres/access/slru.h b/parser/include/postgres/access/slru.h index 97e612cd..02fcb3bc 100644 --- a/parser/include/postgres/access/slru.h +++ b/parser/include/postgres/access/slru.h @@ -128,10 +128,8 @@ typedef struct SlruCtlData { SlruShared shared; - /* - * Bitmask to determine bank number from page number. - */ - bits16 bank_mask; + /* Number of banks in this SLRU. */ + uint16 nbanks; /* * If true, use long segment file names. Otherwise, use short file names. @@ -163,7 +161,6 @@ typedef struct SlruCtlData * it's always the same, it doesn't need to be in shared memory. */ char Dir[64]; - } SlruCtlData; typedef SlruCtlData *SlruCtl; @@ -179,7 +176,7 @@ SimpleLruGetBankLock(SlruCtl ctl, int64 pageno) { int bankno; - bankno = pageno & ctl->bank_mask; + bankno = pageno % ctl->nbanks; return &(ctl->shared->bank_locks[bankno].lock); } diff --git a/parser/include/postgres/access/tableam.h b/parser/include/postgres/access/tableam.h index da661289..7be7887b 100644 --- a/parser/include/postgres/access/tableam.h +++ b/parser/include/postgres/access/tableam.h @@ -137,7 +137,8 @@ typedef enum TU_UpdateIndexes * * xmax is the outdating transaction's XID. If the caller wants to visit the * replacement tuple, it must check that this matches before believing the - * replacement is really a match. + * replacement is really a match. This is InvalidTransactionId if the target + * was !LP_NORMAL (expected only for a TID retrieved from syscache). * * cmax is the outdating command's CID, but only when the failure code is * TM_SelfModified (i.e., something in the current transaction outdated the diff --git a/parser/include/postgres/access/transam.h b/parser/include/postgres/access/transam.h index 28a2d287..07b9be6c 100644 --- a/parser/include/postgres/access/transam.h +++ b/parser/include/postgres/access/transam.h @@ -370,6 +370,49 @@ FullTransactionIdNewer(FullTransactionId a, FullTransactionId b) return b; } +/* + * Compute FullTransactionId for the given TransactionId, assuming xid was + * between [oldestXid, nextXid] at the time when TransamVariables->nextXid was + * nextFullXid. When adding calls, evaluate what prevents xid from preceding + * oldestXid if SetTransactionIdLimit() runs between the collection of xid and + * the collection of nextFullXid. + */ +static inline FullTransactionId +FullTransactionIdFromAllowableAt(FullTransactionId nextFullXid, + TransactionId xid) +{ + uint32 epoch; + + /* Special transaction ID. */ + if (!TransactionIdIsNormal(xid)) + return FullTransactionIdFromEpochAndXid(0, xid); + + Assert(TransactionIdPrecedesOrEquals(xid, + XidFromFullTransactionId(nextFullXid))); + + /* + * The 64 bit result must be <= nextFullXid, since nextFullXid hadn't been + * issued yet when xid was in the past. The xid must therefore be from + * the epoch of nextFullXid or the epoch before. We know this because we + * must remove (by freezing) an XID before assigning the XID half an epoch + * ahead of it. + * + * The unlikely() branch hint is dubious. It's perfect for the first 2^32 + * XIDs of a cluster's life. Right at 2^32 XIDs, misprediction shoots to + * 100%, then improves until perfection returns 2^31 XIDs later. Since + * current callers pass relatively-recent XIDs, expect >90% prediction + * accuracy overall. This favors average latency over tail latency. + */ + epoch = EpochFromFullTransactionId(nextFullXid); + if (unlikely(xid > XidFromFullTransactionId(nextFullXid))) + { + Assert(epoch != 0); + epoch--; + } + + return FullTransactionIdFromEpochAndXid(epoch, xid); +} + #endif /* FRONTEND */ #endif /* TRANSAM_H */ diff --git a/parser/include/postgres/c.h b/parser/include/postgres/c.h index 180fc9df..c5b18692 100644 --- a/parser/include/postgres/c.h +++ b/parser/include/postgres/c.h @@ -435,7 +435,7 @@ typedef void (*pg_funcptr_t) (void); * bool * Boolean value, either true or false. * - * We use stdbool.h if available and its bool has size 1. That's useful for + * We use stdbool.h if bool has size 1 after including it. That's useful for * better compiler and debugger output and for compatibility with third-party * libraries. But PostgreSQL currently cannot deal with bool of other sizes; * there are static assertions around the code to prevent that. diff --git a/parser/include/postgres/catalog/objectaddress.h b/parser/include/postgres/catalog/objectaddress.h index 3a70d80e..c4ba1cb2 100644 --- a/parser/include/postgres/catalog/objectaddress.h +++ b/parser/include/postgres/catalog/objectaddress.h @@ -69,6 +69,10 @@ extern bool get_object_namensp_unique(Oid class_id); extern HeapTuple get_catalog_object_by_oid(Relation catalog, AttrNumber oidcol, Oid objectId); +extern HeapTuple get_catalog_object_by_oid_extended(Relation catalog, + AttrNumber oidcol, + Oid objectId, + bool locktup); extern char *getObjectDescription(const ObjectAddress *object, bool missing_ok); diff --git a/parser/include/postgres/commands/event_trigger.h b/parser/include/postgres/commands/event_trigger.h index 90fc1af5..00cfb397 100644 --- a/parser/include/postgres/commands/event_trigger.h +++ b/parser/include/postgres/commands/event_trigger.h @@ -31,6 +31,12 @@ typedef struct EventTriggerData extern PGDLLIMPORT bool event_triggers; +/* + * Reasons for relation rewrites. + * + * pg_event_trigger_table_rewrite_reason() uses these values, so make sure to + * update the documentation when changing this list. + */ #define AT_REWRITE_ALTER_PERSISTENCE 0x01 #define AT_REWRITE_DEFAULT_VAL 0x02 #define AT_REWRITE_COLUMN_REWRITE 0x04 diff --git a/parser/include/postgres/common/hashfn_unstable.h b/parser/include/postgres/common/hashfn_unstable.h index 0adb0f82..101f1180 100644 --- a/parser/include/postgres/common/hashfn_unstable.h +++ b/parser/include/postgres/common/hashfn_unstable.h @@ -14,8 +14,6 @@ #ifndef HASHFN_UNSTABLE_H #define HASHFN_UNSTABLE_H -#include "port/pg_bitutils.h" -#include "port/pg_bswap.h" /* * fasthash is a modification of code taken from @@ -219,13 +217,6 @@ fasthash_accum(fasthash_state *hs, const char *k, size_t len) #define haszero64(v) \ (((v) - 0x0101010101010101) & ~(v) & 0x8080808080808080) -/* get first byte in memory order */ -#ifdef WORDS_BIGENDIAN -#define firstbyte64(v) ((v) >> 56) -#else -#define firstbyte64(v) ((v) & 0xFF) -#endif - /* * all-purpose workhorse for fasthash_accum_cstring */ @@ -262,33 +253,20 @@ static inline size_t fasthash_accum_cstring_aligned(fasthash_state *hs, const char *str) { const char *const start = str; - uint64 chunk; + size_t remainder; uint64 zero_byte_low; Assert(PointerIsAligned(start, uint64)); /* * For every chunk of input, check for zero bytes before mixing into the - * hash. The chunk with zeros must contain the NUL terminator. We arrange - * so that zero_byte_low tells us not only that a zero exists, but also - * where it is, so we can hash the remainder of the string. - * - * The haszero64 calculation will set bits corresponding to the lowest - * byte where a zero exists, so that suffices for little-endian machines. - * For big-endian machines, we would need bits set for the highest zero - * byte in the chunk, since the trailing junk past the terminator could - * contain additional zeros. haszero64 does not give us that, so we - * byteswap the chunk first. + * hash. The chunk with zeros must contain the NUL terminator. */ for (;;) { - chunk = *(uint64 *) str; + uint64 chunk = *(uint64 *) str; -#ifdef WORDS_BIGENDIAN - zero_byte_low = haszero64(pg_bswap64(chunk)); -#else zero_byte_low = haszero64(chunk); -#endif if (zero_byte_low) break; @@ -297,33 +275,9 @@ fasthash_accum_cstring_aligned(fasthash_state *hs, const char *str) str += FH_SIZEOF_ACCUM; } - if (firstbyte64(chunk) != 0) - { - size_t remainder; - uint64 mask; - - /* - * The byte corresponding to the NUL will be 0x80, so the rightmost - * bit position will be in the range 15, 23, ..., 63. Turn this into - * byte position by dividing by 8. - */ - remainder = pg_rightmost_one_pos64(zero_byte_low) / BITS_PER_BYTE; - - /* - * Create a mask for the remaining bytes so we can combine them into - * the hash. This must have the same result as mixing the remaining - * bytes with fasthash_accum(). - */ -#ifdef WORDS_BIGENDIAN - mask = ~UINT64CONST(0) << BITS_PER_BYTE * (FH_SIZEOF_ACCUM - remainder); -#else - mask = ~UINT64CONST(0) >> BITS_PER_BYTE * (FH_SIZEOF_ACCUM - remainder); -#endif - hs->accum = chunk & mask; - fasthash_combine(hs); - - str += remainder; - } + /* mix in remaining bytes */ + remainder = fasthash_accum_cstring_unaligned(hs, str); + str += remainder; return str - start; } diff --git a/parser/include/postgres/datatype/timestamp.h b/parser/include/postgres/datatype/timestamp.h index 3a37cb66..7d9d2fd0 100644 --- a/parser/include/postgres/datatype/timestamp.h +++ b/parser/include/postgres/datatype/timestamp.h @@ -136,7 +136,7 @@ struct pg_itm_in /* * We allow numeric timezone offsets up to 15:59:59 either way from Greenwich. * Currently, the record holders for wackiest offsets in actual use are zones - * Asia/Manila, at -15:56:00 until 1844, and America/Metlakatla, at +15:13:42 + * Asia/Manila, at -15:56:08 until 1844, and America/Metlakatla, at +15:13:42 * until 1867. If we were to reject such values we would fail to dump and * restore old timestamptz values with these zone settings. */ diff --git a/parser/include/postgres/executor/execdesc.h b/parser/include/postgres/executor/execdesc.h index 0a7274e2..79e8b631 100644 --- a/parser/include/postgres/executor/execdesc.h +++ b/parser/include/postgres/executor/execdesc.h @@ -48,7 +48,7 @@ typedef struct QueryDesc EState *estate; /* executor's query-wide state */ PlanState *planstate; /* tree of per-plan-node state */ - /* This field is set by ExecutorRun */ + /* This field is set by ExecutePlan */ bool already_executed; /* true if previously executed */ /* This is always set NULL by the core system, but plugins can change it */ diff --git a/parser/include/postgres/libpq/libpq-be.h b/parser/include/postgres/libpq/libpq-be.h index 05cb1874..6a1a2274 100644 --- a/parser/include/postgres/libpq/libpq-be.h +++ b/parser/include/postgres/libpq/libpq-be.h @@ -189,7 +189,8 @@ typedef struct Port /* * If GSSAPI is supported and used on this connection, store GSSAPI * information. Even when GSSAPI is not compiled in, store a NULL pointer - * to keep struct offsets the same (for extension ABI compatibility). + * to keep struct offsets of the "SSL structures" below the same (for + * extension ABI compatibility). */ pg_gssinfo *gss; #else @@ -206,8 +207,7 @@ typedef struct Port bool alpn_used; /* - * OpenSSL structures. (Keep these last so that the locations of other - * fields are the same whether or not you build with SSL enabled.) + * OpenSSL structures. */ #ifdef USE_OPENSSL SSL *ssl; @@ -222,6 +222,9 @@ typedef struct Port * There's no API to "unread", the upper layer just places the data in the * Port structure in raw_buf and sets raw_buf_remaining to the amount of * bytes unread and raw_buf_consumed to 0. + * + * NB: the offsets of these fields depend on USE_OPENSSL. These should + * not be accessed in an extension because of the ABI incompatibility. */ char *raw_buf; ssize_t raw_buf_consumed, diff --git a/parser/include/postgres/mb/pg_wchar.h b/parser/include/postgres/mb/pg_wchar.h index 249cd18a..08f6fa6e 100644 --- a/parser/include/postgres/mb/pg_wchar.h +++ b/parser/include/postgres/mb/pg_wchar.h @@ -662,6 +662,7 @@ extern int pg_valid_server_encoding_id(int encoding); * (in addition to the ones just above). The constant tables declared * earlier in this file are also available from libpgcommon. */ +extern void pg_encoding_set_invalid(int encoding, char *dst); extern int pg_encoding_mblen(int encoding, const char *mbstr); extern int pg_encoding_mblen_bounded(int encoding, const char *mbstr); extern int pg_encoding_dsplen(int encoding, const char *mbstr); diff --git a/parser/include/postgres/miscadmin.h b/parser/include/postgres/miscadmin.h index d40ea618..e6a65d9c 100644 --- a/parser/include/postgres/miscadmin.h +++ b/parser/include/postgres/miscadmin.h @@ -346,8 +346,9 @@ typedef enum BackendType /* * Auxiliary processes. These have PGPROC entries, but they are not - * attached to any particular database. There can be only one of each of - * these running at a time. + * attached to any particular database, and cannot run transactions or + * even take heavyweight locks. There can be only one of each of these + * running at a time. * * If you modify these, make sure to update NUM_AUXILIARY_PROCS and the * glossary in the docs. @@ -371,6 +372,7 @@ typedef enum BackendType extern PGDLLIMPORT BackendType MyBackendType; +#define AmRegularBackendProcess() (MyBackendType == B_BACKEND) #define AmAutoVacuumLauncherProcess() (MyBackendType == B_AUTOVAC_LAUNCHER) #define AmAutoVacuumWorkerProcess() (MyBackendType == B_AUTOVAC_WORKER) #define AmBackgroundWorkerProcess() (MyBackendType == B_BG_WORKER) @@ -384,6 +386,10 @@ extern PGDLLIMPORT BackendType MyBackendType; #define AmWalSummarizerProcess() (MyBackendType == B_WAL_SUMMARIZER) #define AmWalWriterProcess() (MyBackendType == B_WAL_WRITER) +#define AmSpecialWorkerProcess() \ + (AmAutoVacuumLauncherProcess() || \ + AmLogicalSlotSyncWorkerProcess()) + extern const char *GetBackendTypeDesc(BackendType backendType); extern void SetDatabasePath(const char *path); @@ -395,7 +401,9 @@ extern char *GetUserNameFromId(Oid roleid, bool noerr); extern Oid GetUserId(void); extern Oid GetOuterUserId(void); extern Oid GetSessionUserId(void); +extern bool GetSessionUserIsSuperuser(void); extern Oid GetAuthenticatedUserId(void); +extern void SetAuthenticatedUserId(Oid userid); extern void GetUserIdAndSecContext(Oid *userid, int *sec_context); extern void SetUserIdAndSecContext(Oid userid, int sec_context); extern bool InLocalUserIdChange(void); diff --git a/parser/include/postgres/nodes/execnodes.h b/parser/include/postgres/nodes/execnodes.h index cd1b1629..17b0ec51 100644 --- a/parser/include/postgres/nodes/execnodes.h +++ b/parser/include/postgres/nodes/execnodes.h @@ -484,6 +484,9 @@ typedef struct ResultRelInfo /* Have the projection and the slots above been initialized? */ bool ri_projectNewInfoValid; + /* updates do LockTuple() before oldtup read; see README.tuplock */ + bool ri_needLockTagTuple; + /* triggers to be fired, if any */ TriggerDesc *ri_TrigDesc; diff --git a/parser/include/postgres/nodes/pathnodes.h b/parser/include/postgres/nodes/pathnodes.h index 2ba297c1..50b09781 100644 --- a/parser/include/postgres/nodes/pathnodes.h +++ b/parser/include/postgres/nodes/pathnodes.h @@ -1101,6 +1101,9 @@ typedef struct IndexOptInfo IndexOptInfo; #define HAVE_INDEXOPTINFO_TYPEDEF 1 #endif +struct IndexPath; /* avoid including pathnodes.h here */ +struct PlannerInfo; /* avoid including pathnodes.h here */ + struct IndexOptInfo { pg_node_attr(no_copy_equal, no_read, no_query_jumble) @@ -1200,7 +1203,7 @@ struct IndexOptInfo bool amcanmarkpos; /* AM's cost estimator */ /* Rather than include amapi.h here, we declare amcostestimate like this */ - void (*amcostestimate) () pg_node_attr(read_write_ignore); + void (*amcostestimate) (struct PlannerInfo *, struct IndexPath *, double, Cost *, Cost *, Selectivity *, double *, double *) pg_node_attr(read_write_ignore); }; /* diff --git a/parser/include/postgres/nodes/pg_list.h b/parser/include/postgres/nodes/pg_list.h index 52df9375..d131350e 100644 --- a/parser/include/postgres/nodes/pg_list.h +++ b/parser/include/postgres/nodes/pg_list.h @@ -485,7 +485,7 @@ for_each_cell_setup(const List *lst, const ListCell *initcell) for (ForEachState var##__state = {(lst), 0}; \ (var##__state.l != NIL && \ var##__state.i < var##__state.l->length && \ - (var = func(&var##__state.l->elements[var##__state.i]), true)); \ + (var = (type pointer) func(&var##__state.l->elements[var##__state.i]), true)); \ var##__state.i++) /* diff --git a/parser/include/postgres/nodes/primnodes.h b/parser/include/postgres/nodes/primnodes.h index ea47652a..94f6b855 100644 --- a/parser/include/postgres/nodes/primnodes.h +++ b/parser/include/postgres/nodes/primnodes.h @@ -1669,15 +1669,19 @@ typedef struct JsonReturning * JsonValueExpr - * representation of JSON value expression (expr [FORMAT JsonFormat]) * - * The actual value is obtained by evaluating formatted_expr. raw_expr is - * only there for displaying the original user-written expression and is not - * evaluated by ExecInterpExpr() and eval_const_expressions_mutator(). + * raw_expr is the user-specified value, while formatted_expr is the value + * obtained by coercing raw_expr to the type required by either the FORMAT + * clause or an enclosing node's RETURNING clause. + * + * When deparsing a JsonValueExpr, get_rule_expr() prints raw_expr. However, + * during the evaluation of a JsonValueExpr, the value of formatted_expr + * takes precedence over that of raw_expr. */ typedef struct JsonValueExpr { NodeTag type; - Expr *raw_expr; /* raw expression */ - Expr *formatted_expr; /* formatted expression */ + Expr *raw_expr; /* user-specified expression */ + Expr *formatted_expr; /* coerced formatted expression */ JsonFormat *format; /* FORMAT clause, if specified */ } JsonValueExpr; diff --git a/parser/include/postgres/parser/parse_coerce.h b/parser/include/postgres/parser/parse_coerce.h index 05afda1b..41ef3943 100644 --- a/parser/include/postgres/parser/parse_coerce.h +++ b/parser/include/postgres/parser/parse_coerce.h @@ -63,6 +63,9 @@ extern Node *coerce_to_specific_type_typmod(ParseState *pstate, Node *node, Oid targetTypeId, int32 targetTypmod, const char *constructName); +extern Node *coerce_null_to_domain(Oid typid, int32 typmod, Oid collation, + int typlen, bool typbyval); + extern int parser_coercion_errposition(ParseState *pstate, int coerce_location, Node *input_expr); diff --git a/parser/include/postgres/pg_config.h b/parser/include/postgres/pg_config.h index d81c3e40..a361c9e0 100644 --- a/parser/include/postgres/pg_config.h +++ b/parser/include/postgres/pg_config.h @@ -116,6 +116,10 @@ don't. */ #define HAVE_DECL_PWRITEV 1 +/* Define to 1 if you have the declaration of `strchrnul', and to 0 if you + don't. */ +#define HAVE_DECL_STRCHRNUL 0 + /* Define to 1 if you have the declaration of `strlcat', and to 0 if you don't. */ #define HAVE_DECL_STRLCAT 1 @@ -388,18 +392,12 @@ /* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */ /* #undef HAVE_SSL_CTX_SET_NUM_TICKETS */ -/* Define to 1 if stdbool.h conforms to C99. */ -#define HAVE_STDBOOL_H 1 - /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 -/* Define to 1 if you have the `strchrnul' function. */ -/* #undef HAVE_STRCHRNUL */ - /* Define to 1 if you have the `strerror_r' function. */ #define HAVE_STRERROR_R 1 @@ -520,9 +518,6 @@ /* Define to 1 if you have XSAVE intrinsics. */ /* #undef HAVE_XSAVE_INTRINSICS */ -/* Define to 1 if the system has the type `_Bool'. */ -#define HAVE__BOOL 1 - /* Define to 1 if your compiler understands __builtin_bswap16. */ #define HAVE__BUILTIN_BSWAP16 1 @@ -578,7 +573,7 @@ #define INT64_MODIFIER "l" /* Define to 1 if `locale_t' requires . */ -#define LOCALE_T_IN_XLOCALE 1 +/* #undef LOCALE_T_IN_XLOCALE */ /* Define as the maximum alignment requirement of any C data type. */ #define MAXIMUM_ALIGNOF 8 @@ -597,7 +592,7 @@ #define PACKAGE_NAME "PostgreSQL" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "PostgreSQL 17.0" +#define PACKAGE_STRING "PostgreSQL 17.4" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "postgresql" @@ -606,7 +601,7 @@ #define PACKAGE_URL "https://www.postgresql.org/" /* Define to the version of this package. */ -#define PACKAGE_VERSION "17.0" +#define PACKAGE_VERSION "17.4" /* Define to the name of a signed 128-bit integer type. */ #define PG_INT128_TYPE __int128 @@ -625,7 +620,7 @@ #define PG_MAJORVERSION_NUM 17 /* PostgreSQL minor version number */ -#define PG_MINORVERSION_NUM 0 +#define PG_MINORVERSION_NUM 4 /* Define to best printf format archetype, usually gnu_printf if available. */ #define PG_PRINTF_ATTRIBUTE printf @@ -634,13 +629,13 @@ #define PG_USE_STDBOOL 1 /* PostgreSQL version as a string */ -#define PG_VERSION "17.0" +#define PG_VERSION "17.4" /* PostgreSQL version as a number */ -#define PG_VERSION_NUM 170000 +#define PG_VERSION_NUM 170004 /* A string containing the version number, platform, and C compiler */ -#define PG_VERSION_STR "PostgreSQL 17.0 (libpg_query)" +#define PG_VERSION_STR "PostgreSQL 17.4 (libpg_query)" /* Define to 1 to allow profiling output to be saved separately for each process. */ @@ -825,9 +820,14 @@ /* Define to how the compiler spells `typeof'. */ /* #undef typeof */ -/* This causes compatibility problems on some Linux distros, with "xlocale.h" not being available */ -#ifndef __APPLE__ +/* + * Assume we don't have xlocale.h on non-MacOS, as not all Linux distros have "xlocale.h" available. + * + * Note this is required on older MacOS to avoid "unknown type name 'locale_t'" + */ #undef LOCALE_T_IN_XLOCALE +#ifdef __APPLE__ +#define LOCALE_T_IN_XLOCALE 1 #endif #undef WCSTOMBS_L_IN_XLOCALE @@ -848,10 +848,18 @@ #undef USE_ARMV8_CRC32C #undef USE_SSE42_CRC32C_WITH_RUNTIME_CHECK -/* Ensure we do not fail on systems that have strchrnul support (FreeBSD, NetBSD and newer glibc) */ +/* + * Ensure we use built-in strchrnul on systems that have strchrnul support (FreeBSD, NetBSD and newer glibc) + * + * Note MacOS 15.4+ also has strchrnul implemented, but is complex to handle correctly, and the code works + * around the double define. + */ #include +#undef HAVE_DECL_STRCHRNUL #if defined(__FreeBSD__) || defined(__NetBSD__) || (defined(__GLIBC__) && ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2)) -#define HAVE_STRCHRNUL +#define HAVE_DECL_STRCHRNUL 1 +#else +#define HAVE_DECL_STRCHRNUL 0 #endif /* 32-bit */ diff --git a/parser/include/postgres/port.h b/parser/include/postgres/port.h index ae115d2d..85cf26c7 100644 --- a/parser/include/postgres/port.h +++ b/parser/include/postgres/port.h @@ -53,6 +53,7 @@ extern char *first_path_var_separator(const char *pathlist); extern void join_path_components(char *ret_path, const char *head, const char *tail); extern void canonicalize_path(char *path); +extern void canonicalize_path_enc(char *path, int encoding); extern void make_native_path(char *filename); extern void cleanup_path(char *path); extern bool path_contains_parent_reference(const char *path); @@ -306,6 +307,33 @@ extern bool rmtree(const char *path, bool rmtopdir); #if defined(WIN32) && !defined(__CYGWIN__) +/* + * We want the 64-bit variant of lseek(). + * + * For Visual Studio, this must be after to avoid messing up its + * lseek() and _lseeki64() function declarations. + * + * For MinGW there is already a macro, so we have to undefine it (depending on + * _FILE_OFFSET_BITS, it may point at its own lseek64, but we don't want to + * count on that being set). + */ +#undef lseek +#define lseek(a,b,c) _lseeki64((a),(b),(c)) + +/* + * We want the 64-bit variant of chsize(). It sets errno and also returns it, + * so convert non-zero result to -1 to match POSIX. + * + * Prevent MinGW from declaring functions, and undefine its macro before we + * define our own. + */ +#ifndef _MSC_VER +#define FTRUNCATE_DEFINED +#include +#undef ftruncate +#endif +#define ftruncate(a,b) (_chsize_s((a),(b)) == 0 ? 0 : -1) + /* * open() and fopen() replacements to allow deletion of open files and * passing of other special options. @@ -488,7 +516,10 @@ extern int pg_check_dir(const char *dir); /* port/pgmkdirp.c */ extern int pg_mkdir_p(char *path, int omode); -/* port/pqsignal.c */ +/* port/pqsignal.c (see also interfaces/libpq/legacy-pqsignal.c) */ +#ifdef FRONTEND +#define pqsignal pqsignal_fe +#endif typedef void (*pqsigfunc) (SIGNAL_ARGS); extern pqsigfunc pqsignal(int signo, pqsigfunc func); diff --git a/parser/include/postgres/port/pg_iovec.h b/parser/include/postgres/port/pg_iovec.h index 7255c1bd..e5fe677b 100644 --- a/parser/include/postgres/port/pg_iovec.h +++ b/parser/include/postgres/port/pg_iovec.h @@ -68,7 +68,7 @@ pg_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) } sum += part; offset += part; - if (part < iov[i].iov_len) + if ((size_t) part < iov[i].iov_len) return sum; } return sum; @@ -107,7 +107,7 @@ pg_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) } sum += part; offset += part; - if (part < iov[i].iov_len) + if ((size_t) part < iov[i].iov_len) return sum; } return sum; diff --git a/parser/include/postgres/port/win32_port.h b/parser/include/postgres/port/win32_port.h index 87059b86..9a1701b3 100644 --- a/parser/include/postgres/port/win32_port.h +++ b/parser/include/postgres/port/win32_port.h @@ -79,8 +79,6 @@ /* Must be here to avoid conflicting with prototype in windows.h */ #define mkdir(a,b) mkdir(a) -#define ftruncate(a,b) chsize(a,b) - /* Windows doesn't have fsync() as such, use _commit() */ #define fsync(fd) _commit(fd) diff --git a/parser/include/postgres/replication/reorderbuffer.h b/parser/include/postgres/replication/reorderbuffer.h index 851a001c..7de50462 100644 --- a/parser/include/postgres/replication/reorderbuffer.h +++ b/parser/include/postgres/replication/reorderbuffer.h @@ -478,45 +478,38 @@ typedef void (*ReorderBufferRollbackPreparedCB) (ReorderBuffer *rb, TimestampTz prepare_time); /* start streaming transaction callback signature */ -typedef void (*ReorderBufferStreamStartCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferStreamStartCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr first_lsn); /* stop streaming transaction callback signature */ -typedef void (*ReorderBufferStreamStopCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferStreamStopCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr last_lsn); /* discard streamed transaction callback signature */ -typedef void (*ReorderBufferStreamAbortCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferStreamAbortCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr abort_lsn); /* prepare streamed transaction callback signature */ -typedef void (*ReorderBufferStreamPrepareCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferStreamPrepareCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr prepare_lsn); /* commit streamed transaction callback signature */ -typedef void (*ReorderBufferStreamCommitCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferStreamCommitCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr commit_lsn); /* stream change callback signature */ -typedef void (*ReorderBufferStreamChangeCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferStreamChangeCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change); /* stream message callback signature */ -typedef void (*ReorderBufferStreamMessageCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferStreamMessageCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr message_lsn, bool transactional, @@ -524,16 +517,14 @@ typedef void (*ReorderBufferStreamMessageCB) ( const char *message); /* stream truncate callback signature */ -typedef void (*ReorderBufferStreamTruncateCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferStreamTruncateCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, int nrelations, Relation relations[], ReorderBufferChange *change); /* update progress txn callback signature */ -typedef void (*ReorderBufferUpdateProgressTxnCB) ( - ReorderBuffer *rb, +typedef void (*ReorderBufferUpdateProgressTxnCB) (ReorderBuffer *rb, ReorderBufferTXN *txn, XLogRecPtr lsn); diff --git a/parser/include/postgres/replication/slot.h b/parser/include/postgres/replication/slot.h index c9675ee8..07561bc4 100644 --- a/parser/include/postgres/replication/slot.h +++ b/parser/include/postgres/replication/slot.h @@ -202,7 +202,11 @@ typedef struct ReplicationSlot */ XLogRecPtr last_saved_confirmed_flush; - /* The time since the slot has become inactive */ + /* + * The time when the slot became inactive. For synced slots on a standby + * server, it represents the time when slot synchronization was most + * recently stopped. + */ TimestampTz inactive_since; } ReplicationSlot; diff --git a/parser/include/postgres/storage/lockdefs.h b/parser/include/postgres/storage/lockdefs.h index 934ba84f..810b297e 100644 --- a/parser/include/postgres/storage/lockdefs.h +++ b/parser/include/postgres/storage/lockdefs.h @@ -47,6 +47,8 @@ typedef int LOCKMODE; #define MaxLockMode 8 /* highest standard lock mode */ +/* See README.tuplock section "Locking to write inplace-updated tables" */ +#define InplaceUpdateTupleLock ExclusiveLock /* WAL representation of an AccessExclusiveLock on a table */ typedef struct xl_standby_lock diff --git a/parser/include/postgres/storage/proc.h b/parser/include/postgres/storage/proc.h index 9488bf18..bf0a714d 100644 --- a/parser/include/postgres/storage/proc.h +++ b/parser/include/postgres/storage/proc.h @@ -212,7 +212,7 @@ struct PGPROC Oid tempNamespaceId; /* OID of temp schema this backend is * using */ - bool isBackgroundWorker; /* true if background worker. */ + bool isBackgroundWorker; /* true if not a regular backend. */ /* * While in hot standby mode, shows that a conflict signal has been sent @@ -313,19 +313,6 @@ struct PGPROC extern PGDLLIMPORT PGPROC *MyProc; -/* Proc number of this backend. Equal to GetNumberFromPGProc(MyProc). */ -extern PGDLLIMPORT ProcNumber MyProcNumber; - -/* Our parallel session leader, or INVALID_PROC_NUMBER if none */ -extern PGDLLIMPORT ProcNumber ParallelLeaderProcNumber; - -/* - * The proc number to use for our session's temp relations is normally our own, - * but parallel workers should use their leader's ID. - */ -#define ProcNumberForTempRelations() \ - (ParallelLeaderProcNumber == INVALID_PROC_NUMBER ? MyProcNumber : ParallelLeaderProcNumber) - /* * There is one ProcGlobal struct for the whole database cluster. * @@ -404,7 +391,7 @@ typedef struct PROC_HDR uint32 allProcCount; /* Head of list of free PGPROC structures */ dlist_head freeProcs; - /* Head of list of autovacuum's free PGPROC structures */ + /* Head of list of autovacuum & special worker free PGPROC structures */ dlist_head autovacFreeProcs; /* Head of list of bgworker free PGPROC structures */ dlist_head bgworkerFreeProcs; @@ -434,9 +421,19 @@ extern PGDLLIMPORT PGPROC *PreparedXactProcs; #define GetPGProcByNumber(n) (&ProcGlobal->allProcs[(n)]) #define GetNumberFromPGProc(proc) ((proc) - &ProcGlobal->allProcs[0]) +/* + * We set aside some extra PGPROC structures for "special worker" processes, + * which are full-fledged backends (they can run transactions) + * but are unique animals that there's never more than one of. + * Currently there are two such processes: the autovacuum launcher + * and the slotsync worker. + */ +#define NUM_SPECIAL_WORKER_PROCS 2 + /* * We set aside some extra PGPROC structures for auxiliary processes, - * ie things that aren't full-fledged backends but need shmem access. + * ie things that aren't full-fledged backends (they cannot run transactions + * or take heavyweight locks) but need shmem access. * * Background writer, checkpointer, WAL writer, WAL summarizer, and archiver * run during normal operation. Startup process and WAL receiver also consume diff --git a/parser/include/postgres/storage/smgr.h b/parser/include/postgres/storage/smgr.h index e15b20a5..3856d1d4 100644 --- a/parser/include/postgres/storage/smgr.h +++ b/parser/include/postgres/storage/smgr.h @@ -103,8 +103,11 @@ extern void smgrwriteback(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, BlockNumber nblocks); extern BlockNumber smgrnblocks(SMgrRelation reln, ForkNumber forknum); extern BlockNumber smgrnblocks_cached(SMgrRelation reln, ForkNumber forknum); -extern void smgrtruncate(SMgrRelation reln, ForkNumber *forknum, - int nforks, BlockNumber *nblocks); +extern void smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, + BlockNumber *nblocks); +extern void smgrtruncate2(SMgrRelation reln, ForkNumber *forknum, int nforks, + BlockNumber *old_nblocks, + BlockNumber *nblocks); extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum); extern void smgrregistersync(SMgrRelation reln, ForkNumber forknum); extern void AtEOXact_SMgr(void); diff --git a/parser/include/postgres/utils/catcache.h b/parser/include/postgres/utils/catcache.h index 3fb9647b..99169a93 100644 --- a/parser/include/postgres/utils/catcache.h +++ b/parser/include/postgres/utils/catcache.h @@ -220,6 +220,7 @@ extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys, extern void ReleaseCatCacheList(CatCList *list); extern void ResetCatalogCaches(void); +extern void ResetCatalogCachesExt(bool debug_discard); extern void CatalogCacheFlushCatalog(Oid catId); extern void CatCacheInvalidate(CatCache *cache, uint32 hashValue); extern void PrepareToInvalidateCacheTuple(Relation relation, diff --git a/parser/include/postgres/utils/pgstat_internal.h b/parser/include/postgres/utils/pgstat_internal.h index dbbca316..22106778 100644 --- a/parser/include/postgres/utils/pgstat_internal.h +++ b/parser/include/postgres/utils/pgstat_internal.h @@ -93,6 +93,19 @@ typedef struct PgStatShared_HashEntry */ pg_atomic_uint32 refcount; + /* + * Counter tracking the number of times the entry has been reused. + * + * Set to 0 when the entry is created, and incremented by one each time + * the shared entry is reinitialized with pgstat_reinit_entry(). + * + * May only be incremented / decremented while holding at least a shared + * lock on the dshash partition containing the entry. Like refcount, it + * needs to be an atomic variable because multiple backends can increment + * the generation with just a shared lock. + */ + pg_atomic_uint32 generation; + /* * Pointer to shared stats. The stats entry always starts with * PgStatShared_Common, embedded in a larger struct containing the @@ -132,6 +145,12 @@ typedef struct PgStat_EntryRef */ PgStatShared_Common *shared_stats; + /* + * Copy of PgStatShared_HashEntry->generation, keeping locally track of + * the shared stats entry "generation" retrieved (number of times reused). + */ + uint32 generation; + /* * Pending statistics data that will need to be flushed to shared memory * stats eventually. Each stats kind utilizing pending data defines what diff --git a/parser/include/postgres/utils/portal.h b/parser/include/postgres/utils/portal.h index 29f49829..6c635150 100644 --- a/parser/include/postgres/utils/portal.h +++ b/parser/include/postgres/utils/portal.h @@ -145,7 +145,7 @@ typedef struct PortalData /* Features/options */ PortalStrategy strategy; /* see above */ int cursorOptions; /* DECLARE CURSOR option bits */ - bool run_once; /* portal will only be run once */ + bool run_once; /* unused */ /* Status data */ PortalStatus status; /* see above */ diff --git a/parser/include/postgres/utils/syscache.h b/parser/include/postgres/utils/syscache.h index 03a27dd0..b541911c 100644 --- a/parser/include/postgres/utils/syscache.h +++ b/parser/include/postgres/utils/syscache.h @@ -43,9 +43,14 @@ extern HeapTuple SearchSysCache4(int cacheId, extern void ReleaseSysCache(HeapTuple tuple); +extern HeapTuple SearchSysCacheLocked1(int cacheId, + Datum key1); + /* convenience routines */ extern HeapTuple SearchSysCacheCopy(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4); +extern HeapTuple SearchSysCacheLockedCopy1(int cacheId, + Datum key1); extern bool SearchSysCacheExists(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4); extern Oid GetSysCacheOid(int cacheId, AttrNumber oidcol, diff --git a/parser/postgres_deparse.c b/parser/postgres_deparse.c index fd4e0add..272b3524 100644 --- a/parser/postgres_deparse.c +++ b/parser/postgres_deparse.c @@ -20,12 +20,7 @@ typedef enum DeparseNodeContext { DEPARSE_NODE_CONTEXT_NONE, // Parent node type (and sometimes field) DEPARSE_NODE_CONTEXT_INSERT_RELATION, - DEPARSE_NODE_CONTEXT_INSERT_ON_CONFLICT, - DEPARSE_NODE_CONTEXT_UPDATE, - DEPARSE_NODE_CONTEXT_RETURNING, DEPARSE_NODE_CONTEXT_A_EXPR, - DEPARSE_NODE_CONTEXT_XMLATTRIBUTES, - DEPARSE_NODE_CONTEXT_XMLNAMESPACES, DEPARSE_NODE_CONTEXT_CREATE_TYPE, DEPARSE_NODE_CONTEXT_ALTER_TYPE, DEPARSE_NODE_CONTEXT_SET_STATEMENT, @@ -186,9 +181,9 @@ static void deparseReplicaIdentityStmt(StringInfo str, ReplicaIdentityStmt *repl static void deparseRangeTableSample(StringInfo str, RangeTableSample *range_table_sample); static void deparseRangeTableFunc(StringInfo str, RangeTableFunc* range_table_func); static void deparseGroupingSet(StringInfo str, GroupingSet *grouping_set); -static void deparseFuncCall(StringInfo str, FuncCall *func_call); +static void deparseFuncCall(StringInfo str, FuncCall *func_call, DeparseNodeContext context); static void deparseMinMaxExpr(StringInfo str, MinMaxExpr *min_max_expr); -static void deparseXmlExpr(StringInfo str, XmlExpr* xml_expr); +static void deparseXmlExpr(StringInfo str, XmlExpr* xml_expr, DeparseNodeContext context); static void deparseXmlSerialize(StringInfo str, XmlSerialize *xml_serialize); static void deparseJsonIsPredicate(StringInfo str, JsonIsPredicate *json_is_predicate); static void deparseJsonObjectAgg(StringInfo str, JsonObjectAgg *json_object_agg); @@ -266,12 +261,12 @@ static void deparseAnyNameSkipLast(StringInfo str, List *parts) } // "func_expr" in gram.y -static void deparseFuncExpr(StringInfo str, Node *node) +static void deparseFuncExpr(StringInfo str, Node *node, DeparseNodeContext context) { switch (nodeTag(node)) { case T_FuncCall: - deparseFuncCall(str, castNode(FuncCall, node)); + deparseFuncCall(str, castNode(FuncCall, node), context); break; case T_SQLValueFunction: deparseSQLValueFunction(str, castNode(SQLValueFunction, node)); @@ -283,7 +278,7 @@ static void deparseFuncExpr(StringInfo str, Node *node) deparseCoalesceExpr(str, castNode(CoalesceExpr, node)); break; case T_XmlExpr: - deparseXmlExpr(str, castNode(XmlExpr, node)); + deparseXmlExpr(str, castNode(XmlExpr, node), context); break; case T_XmlSerialize: deparseXmlSerialize(str, castNode(XmlSerialize, node)); @@ -313,7 +308,7 @@ static void deparseFuncExpr(StringInfo str, Node *node) static void deparseCExpr(StringInfo str, Node *node); // "a_expr" in gram.y -static void deparseExpr(StringInfo str, Node *node) +static void deparseExpr(StringInfo str, Node *node, DeparseNodeContext context) { if (node == NULL) return; @@ -337,7 +332,7 @@ static void deparseExpr(StringInfo str, Node *node) deparseCollateClause(str, castNode(CollateClause, node)); break; case T_A_Expr: - deparseAExpr(str, castNode(A_Expr, node), DEPARSE_NODE_CONTEXT_NONE); + deparseAExpr(str, castNode(A_Expr, node), DEPARSE_NODE_CONTEXT_A_EXPR); break; case T_BoolExpr: deparseBoolExpr(str, castNode(BoolExpr, node)); @@ -380,7 +375,7 @@ static void deparseExpr(StringInfo str, Node *node) case T_JsonObjectConstructor: case T_JsonArrayConstructor: case T_JsonArrayQueryConstructor: - deparseFuncExpr(str, node); + deparseFuncExpr(str, node, context); break; default: // Note that this is also the fallthrough for deparseBExpr and deparseCExpr @@ -394,7 +389,7 @@ static void deparseExpr(StringInfo str, Node *node) static void deparseBExpr(StringInfo str, Node *node) { if (IsA(node, XmlExpr)) { - deparseXmlExpr(str, castNode(XmlExpr, node)); + deparseXmlExpr(str, castNode(XmlExpr, node), DEPARSE_NODE_CONTEXT_NONE); return; } @@ -418,6 +413,24 @@ static void deparseBExpr(StringInfo str, Node *node) deparseCExpr(str, node); } +// "AexprConst" in gram.y +static void deparseAexprConst(StringInfo str, Node *node) +{ + switch (nodeTag(node)) + { + case T_A_Const: + deparseAConst(str, castNode(A_Const, node)); + break; + case T_TypeCast: + deparseTypeCast(str, castNode(TypeCast, node), DEPARSE_NODE_CONTEXT_NONE); + break; + default: + elog(ERROR, "deparse: unpermitted node type in AexprConst: %d", + (int) nodeTag(node)); + break; + } +} + // "c_expr" in gram.y static void deparseCExpr(StringInfo str, Node *node) { @@ -461,11 +474,12 @@ static void deparseCExpr(StringInfo str, Node *node) case T_JsonObjectConstructor: case T_JsonArrayConstructor: case T_JsonArrayQueryConstructor: - deparseFuncExpr(str, node); + deparseFuncExpr(str, node, DEPARSE_NODE_CONTEXT_NONE); break; default: appendStringInfoChar(str, '('); - deparseExpr(str, node); + // Because we wrap this in parenthesis, the expression inside follows "a_expr" parser rules + deparseExpr(str, node, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); break; } @@ -477,7 +491,7 @@ static void deparseExprList(StringInfo str, List *exprs) ListCell *lc; foreach(lc, exprs) { - deparseExpr(str, lfirst(lc)); + deparseExpr(str, lfirst(lc), DEPARSE_NODE_CONTEXT_A_EXPR); if (lnext(exprs, lc)) appendStringInfoString(str, ", "); } @@ -1583,7 +1597,7 @@ static void deparseTargetList(StringInfo str, List *l) else if (IsA(res_target->val, ColumnRef)) deparseColumnRef(str, castNode(ColumnRef, res_target->val)); else - deparseExpr(str, res_target->val); + deparseExpr(str, res_target->val, DEPARSE_NODE_CONTEXT_A_EXPR); if (res_target->name != NULL) { appendStringInfoString(str, " AS "); @@ -1621,7 +1635,7 @@ static void deparseXmlAttributeList(StringInfo str, List *l) ResTarget *res_target = castNode(ResTarget, lfirst(lc)); Assert(res_target->val != NULL); - deparseExpr(str, res_target->val); + deparseExpr(str, res_target->val, DEPARSE_NODE_CONTEXT_A_EXPR); if (res_target->name != NULL) { @@ -1647,7 +1661,7 @@ static void deparseXmlNamespaceList(StringInfo str, List *l) if (res_target->name == NULL) appendStringInfoString(str, "DEFAULT "); - deparseExpr(str, res_target->val); + deparseExpr(str, res_target->val, DEPARSE_NODE_CONTEXT_NONE /* b_expr */); if (res_target->name != NULL) { @@ -1725,7 +1739,7 @@ static void deparseWhereClause(StringInfo str, Node *node) if (node != NULL) { appendStringInfoString(str, "WHERE "); - deparseExpr(str, node); + deparseExpr(str, node, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } } @@ -1745,7 +1759,7 @@ static void deparseWhereOrCurrentClause(StringInfo str, Node *node) appendStringInfoString(str, "CURRENT OF "); appendStringInfoString(str, quote_identifier(current_of_expr->cursor_name)); } else { - deparseExpr(str, node); + deparseExpr(str, node, DEPARSE_NODE_CONTEXT_A_EXPR); } appendStringInfoChar(str, ' '); @@ -1761,7 +1775,7 @@ static void deparseGroupByList(StringInfo str, List *l) if (IsA(lfirst(lc), GroupingSet)) deparseGroupingSet(str, castNode(GroupingSet, lfirst(lc))); else - deparseExpr(str, lfirst(lc)); + deparseExpr(str, lfirst(lc), DEPARSE_NODE_CONTEXT_A_EXPR); if (lnext(l, lc)) appendStringInfoString(str, ", "); @@ -1831,11 +1845,11 @@ static void deparseFuncArgExpr(StringInfo str, Node *node) NamedArgExpr *named_arg_expr = castNode(NamedArgExpr, node); appendStringInfoString(str, named_arg_expr->name); appendStringInfoString(str, " := "); - deparseExpr(str, (Node *) named_arg_expr->arg); + deparseExpr(str, (Node *) named_arg_expr->arg, DEPARSE_NODE_CONTEXT_A_EXPR); } else { - deparseExpr(str, node); + deparseExpr(str, node, DEPARSE_NODE_CONTEXT_A_EXPR); } } @@ -1875,14 +1889,14 @@ static void deparseSetClauseList(StringInfo str, List *target_list) appendStringInfoString(str, ", "); } appendStringInfoString(str, ") = "); - deparseExpr(str, r->source); + deparseExpr(str, r->source, DEPARSE_NODE_CONTEXT_A_EXPR); skip_next_n_elems = r->ncolumns - 1; } else { deparseSetTarget(str, res_target); appendStringInfoString(str, " = "); - deparseExpr(str, res_target->val); + deparseExpr(str, res_target->val, DEPARSE_NODE_CONTEXT_A_EXPR); } } } @@ -1893,7 +1907,7 @@ static void deparseFuncExprWindowless(StringInfo str, Node* node) switch (nodeTag(node)) { case T_FuncCall: - deparseFuncCall(str, castNode(FuncCall, node)); + deparseFuncCall(str, castNode(FuncCall, node), DEPARSE_NODE_CONTEXT_NONE /* we don't know which kind of expression */); break; case T_SQLValueFunction: deparseSQLValueFunction(str, castNode(SQLValueFunction, node)); @@ -1908,7 +1922,7 @@ static void deparseFuncExprWindowless(StringInfo str, Node* node) deparseMinMaxExpr(str, castNode(MinMaxExpr, node)); break; case T_XmlExpr: - deparseXmlExpr(str, castNode(XmlExpr, node)); + deparseXmlExpr(str, castNode(XmlExpr, node), DEPARSE_NODE_CONTEXT_NONE /* we don't know which kind of expression */); break; case T_XmlSerialize: deparseXmlSerialize(str, castNode(XmlSerialize, node)); @@ -1955,7 +1969,7 @@ static void deparseIndexElem(StringInfo str, IndexElem* index_elem) break; default: appendStringInfoChar(str, '('); - deparseExpr(str, index_elem->expr); + deparseExpr(str, index_elem->expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } } @@ -2305,7 +2319,7 @@ static void deparseSelectStmt(StringInfo str, SelectStmt *stmt) if (stmt->havingClause != NULL) { appendStringInfoString(str, "HAVING "); - deparseExpr(str, stmt->havingClause); + deparseExpr(str, stmt->havingClause, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } @@ -2388,7 +2402,7 @@ static void deparseSelectStmt(StringInfo str, SelectStmt *stmt) else if (stmt->limitOption == LIMIT_OPTION_WITH_TIES) deparseCExpr(str, stmt->limitCount); else - deparseExpr(str, stmt->limitCount); + deparseExpr(str, stmt->limitCount, DEPARSE_NODE_CONTEXT_NONE /* c_expr */); appendStringInfoChar(str, ' '); @@ -2399,7 +2413,7 @@ static void deparseSelectStmt(StringInfo str, SelectStmt *stmt) if (stmt->limitOffset != NULL) { appendStringInfoString(str, "OFFSET "); - deparseExpr(str, stmt->limitOffset); + deparseExpr(str, stmt->limitOffset, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } @@ -2525,7 +2539,7 @@ static void deparseAConst(StringInfo str, A_Const *a_const) deparseValue(str, val, DEPARSE_NODE_CONTEXT_CONSTANT); } -static void deparseFuncCall(StringInfo str, FuncCall *func_call) +static void deparseFuncCall(StringInfo str, FuncCall *func_call, DeparseNodeContext context) { const ListCell *lc = NULL; @@ -2541,13 +2555,13 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) * keyword parameter style when its called as a keyword, not as a regular function (i.e. pg_catalog.overlay) */ appendStringInfoString(str, "OVERLAY("); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " PLACING "); - deparseExpr(str, lsecond(func_call->args)); + deparseExpr(str, lsecond(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " FROM "); - deparseExpr(str, lthird(func_call->args)); + deparseExpr(str, lthird(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " FOR "); - deparseExpr(str, lfourth(func_call->args)); + deparseExpr(str, lfourth(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); return; } else if (func_call->funcformat == COERCE_SQL_SYNTAX && @@ -2561,13 +2575,13 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) */ Assert(list_length(func_call->args) == 2 || list_length(func_call->args) == 3); appendStringInfoString(str, "SUBSTRING("); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " FROM "); - deparseExpr(str, lsecond(func_call->args)); + deparseExpr(str, lsecond(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); if (list_length(func_call->args) == 3) { appendStringInfoString(str, " FOR "); - deparseExpr(str, lthird(func_call->args)); + deparseExpr(str, lthird(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); } appendStringInfoChar(str, ')'); return; @@ -2599,11 +2613,11 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) * keyword parameter style when its called as a keyword, not as a regular function (i.e. pg_catalog.overlay) */ appendStringInfoString(str, "overlay("); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " placing "); - deparseExpr(str, lsecond(func_call->args)); + deparseExpr(str, lsecond(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " from "); - deparseExpr(str, lthird(func_call->args)); + deparseExpr(str, lthird(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); return; } else if (func_call->funcformat == COERCE_SQL_SYNTAX && @@ -2617,7 +2631,7 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) * keyword parameter style when its called as a keyword, not as a regular function (i.e. pg_catalog.overlay) */ appendStringInfoString(str, "collation for ("); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); return; } else if (func_call->funcformat == COERCE_SQL_SYNTAX && @@ -2631,9 +2645,9 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) * keyword parameter style when its called as a keyword, not as a regular function (i.e. pg_catalog.extract) */ appendStringInfoString(str, "extract ("); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " FROM "); - deparseExpr(str, lsecond(func_call->args)); + deparseExpr(str, lsecond(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); return; } else if (func_call->funcformat == COERCE_SQL_SYNTAX && @@ -2648,16 +2662,16 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) * format: (start_1, end_1) overlaps (start_2, end_2) */ appendStringInfoChar(str, '('); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ", "); - deparseExpr(str, lsecond(func_call->args)); + deparseExpr(str, lsecond(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); appendStringInfoString(str, "overlaps "); appendStringInfoChar(str, '('); - deparseExpr(str, lthird(func_call->args)); + deparseExpr(str, lthird(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ", "); - deparseExpr(str, lfourth(func_call->args)); + deparseExpr(str, lfourth(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); return; } else if (func_call->funcformat == COERCE_SQL_SYNTAX && @@ -2684,9 +2698,9 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) appendStringInfoString(str, "TRAILING "); if (list_length(func_call->args) == 2) - deparseExpr(str, lsecond(func_call->args)); + deparseExpr(str, lsecond(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " FROM "); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); return; } else if (func_call->funcformat == COERCE_SQL_SYNTAX && @@ -2709,11 +2723,16 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) else e = lsecond(func_call->args); + // If we're not inside an a_expr context, we must add wrapping parenthesis around the AT ... syntax + if (context != DEPARSE_NODE_CONTEXT_A_EXPR) { + appendStringInfoChar(str, '('); + } + if (IsA(e, A_Expr)) { appendStringInfoChar(str, '('); } - deparseExpr(str, (Node*) e); + deparseExpr(str, (Node*) e, DEPARSE_NODE_CONTEXT_A_EXPR); if (IsA(e, A_Expr)) { appendStringInfoChar(str, ')'); @@ -2723,8 +2742,13 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) appendStringInfoString(str, " AT LOCAL"); else { appendStringInfoString(str, " AT TIME ZONE "); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); + } + + if (context != DEPARSE_NODE_CONTEXT_A_EXPR) { + appendStringInfoChar(str, ')'); } + return; } else if (func_call->funcformat == COERCE_SQL_SYNTAX && list_length(func_call->funcname) == 2 && @@ -2738,7 +2762,7 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) Assert(list_length(func_call->args) == 1 || list_length(func_call->args) == 2); appendStringInfoString(str, "normalize ("); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); if (list_length(func_call->args) == 2) { appendStringInfoString(str, ", "); @@ -2759,7 +2783,7 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) */ Assert(list_length(func_call->args) == 1 || list_length(func_call->args) == 2); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " IS "); if (list_length(func_call->args) == 2) { @@ -2776,9 +2800,9 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) list_length(func_call->args) == 2) { appendStringInfoString(str, "xmlexists ("); - deparseExpr(str, linitial(func_call->args)); + deparseExpr(str, linitial(func_call->args), DEPARSE_NODE_CONTEXT_NONE /* c_expr */); appendStringInfoString(str, " PASSING "); - deparseExpr(str, lsecond(func_call->args)); + deparseExpr(str, lsecond(func_call->args), DEPARSE_NODE_CONTEXT_NONE /* c_expr */); appendStringInfoChar(str, ')'); return; } else if (func_call->funcformat == COERCE_SQL_SYNTAX && @@ -2832,7 +2856,7 @@ static void deparseFuncCall(StringInfo str, FuncCall *func_call) if (func_call->agg_filter) { appendStringInfoString(str, "FILTER (WHERE "); - deparseExpr(str, func_call->agg_filter); + deparseExpr(str, func_call->agg_filter, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } @@ -2899,13 +2923,13 @@ static void deparseWindowDef(StringInfo str, WindowDef* window_def) else if (window_def->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING) { Assert(window_def->startOffset != NULL); - deparseExpr(str, window_def->startOffset); + deparseExpr(str, window_def->startOffset, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " PRECEDING "); } else if (window_def->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING) { Assert(window_def->startOffset != NULL); - deparseExpr(str, window_def->startOffset); + deparseExpr(str, window_def->startOffset, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " FOLLOWING "); } @@ -2929,13 +2953,13 @@ static void deparseWindowDef(StringInfo str, WindowDef* window_def) else if (window_def->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING) { Assert(window_def->endOffset != NULL); - deparseExpr(str, window_def->endOffset); + deparseExpr(str, window_def->endOffset, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " PRECEDING "); } else if (window_def->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING) { Assert(window_def->endOffset != NULL); - deparseExpr(str, window_def->endOffset); + deparseExpr(str, window_def->endOffset, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " FOLLOWING "); } } @@ -2973,7 +2997,7 @@ static void deparseSubLink(StringInfo str, SubLink* sub_link) appendStringInfoChar(str, ')'); return; case ALL_SUBLINK: - deparseExpr(str, sub_link->testexpr); + deparseExpr(str, sub_link->testexpr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); deparseSubqueryOp(str, sub_link->operName); appendStringInfoString(str, " ALL ("); @@ -2981,7 +3005,7 @@ static void deparseSubLink(StringInfo str, SubLink* sub_link) appendStringInfoChar(str, ')'); return; case ANY_SUBLINK: - deparseExpr(str, sub_link->testexpr); + deparseExpr(str, sub_link->testexpr, DEPARSE_NODE_CONTEXT_A_EXPR); if (list_length(sub_link->operName) > 0) { appendStringInfoChar(str, ' '); @@ -3021,6 +3045,7 @@ static void deparseSubLink(StringInfo str, SubLink* sub_link) } } +// This handles "A_Expr" parse tree objects, which are a subset of the rules in "a_expr" (handled by deparseExpr) static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext context) { ListCell *lc; @@ -3032,15 +3057,11 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont switch (a_expr->kind) { case AEXPR_OP: /* normal operator */ { - bool need_outer_parens = context == DEPARSE_NODE_CONTEXT_A_EXPR; - - if (need_outer_parens) - appendStringInfoChar(str, '('); if (a_expr->lexpr != NULL) { if (need_lexpr_parens) appendStringInfoChar(str, '('); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); if (need_lexpr_parens) appendStringInfoChar(str, ')'); appendStringInfoChar(str, ' '); @@ -3051,29 +3072,26 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont appendStringInfoChar(str, ' '); if (need_rexpr_parens) appendStringInfoChar(str, '('); - deparseExpr(str, a_expr->rexpr); + deparseExpr(str, a_expr->rexpr, context); if (need_rexpr_parens) appendStringInfoChar(str, ')'); } - - if (need_outer_parens) - appendStringInfoChar(str, ')'); } return; case AEXPR_OP_ANY: /* scalar op ANY (array) */ - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoChar(str, ' '); deparseSubqueryOp(str, a_expr->name); appendStringInfoString(str, " ANY("); - deparseExpr(str, a_expr->rexpr); + deparseExpr(str, a_expr->rexpr, context); appendStringInfoChar(str, ')'); return; case AEXPR_OP_ALL: /* scalar op ALL (array) */ - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoChar(str, ' '); deparseSubqueryOp(str, a_expr->name); appendStringInfoString(str, " ALL("); - deparseExpr(str, a_expr->rexpr); + deparseExpr(str, a_expr->rexpr, context); appendStringInfoChar(str, ')'); return; case AEXPR_DISTINCT: /* IS DISTINCT FROM - name must be "=" */ @@ -3083,13 +3101,13 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont if (need_lexpr_parens) appendStringInfoChar(str, '('); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); if (need_lexpr_parens) appendStringInfoChar(str, ')'); appendStringInfoString(str, " IS DISTINCT FROM "); if (need_rexpr_parens) appendStringInfoChar(str, '('); - deparseExpr(str, a_expr->rexpr); + deparseExpr(str, a_expr->rexpr, context); if (need_rexpr_parens) appendStringInfoChar(str, ')'); return; @@ -3098,9 +3116,9 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont Assert(IsA(linitial(a_expr->name), String)); Assert(strcmp(strVal(linitial(a_expr->name)), "=") == 0); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoString(str, " IS NOT DISTINCT FROM "); - deparseExpr(str, a_expr->rexpr); + deparseExpr(str, a_expr->rexpr, context); return; case AEXPR_NULLIF: /* NULLIF - name must be "=" */ Assert(list_length(a_expr->name) == 1); @@ -3108,16 +3126,16 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont Assert(strcmp(strVal(linitial(a_expr->name)), "=") == 0); appendStringInfoString(str, "NULLIF("); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoString(str, ", "); - deparseExpr(str, a_expr->rexpr); + deparseExpr(str, a_expr->rexpr, context); appendStringInfoChar(str, ')'); return; case AEXPR_IN: /* [NOT] IN - name must be "=" or "<>" */ Assert(list_length(a_expr->name) == 1); Assert(IsA(linitial(a_expr->name), String)); Assert(IsA(a_expr->rexpr, List)); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoChar(str, ' '); name = ((union ValUnion *) linitial(a_expr->name))->sval.sval; if (strcmp(name, "=") == 0) { @@ -3137,7 +3155,7 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont case AEXPR_LIKE: /* [NOT] LIKE - name must be "~~" or "!~~" */ Assert(list_length(a_expr->name) == 1); Assert(IsA(linitial(a_expr->name), String)); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoChar(str, ' '); name = ((union ValUnion *) linitial(a_expr->name))->sval.sval; @@ -3149,12 +3167,12 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont Assert(false); } - deparseExpr(str, a_expr->rexpr); + deparseExpr(str, a_expr->rexpr, context); return; case AEXPR_ILIKE: /* [NOT] ILIKE - name must be "~~*" or "!~~*" */ Assert(list_length(a_expr->name) == 1); Assert(IsA(linitial(a_expr->name), String)); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoChar(str, ' '); name = ((union ValUnion *) linitial(a_expr->name))->sval.sval; @@ -3166,12 +3184,12 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont Assert(false); } - deparseExpr(str, a_expr->rexpr); + deparseExpr(str, a_expr->rexpr, context); return; case AEXPR_SIMILAR: /* [NOT] SIMILAR - name must be "~" or "!~" */ Assert(list_length(a_expr->name) == 1); Assert(IsA(linitial(a_expr->name), String)); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoChar(str, ' '); name = ((union ValUnion *) linitial(a_expr->name))->sval.sval; @@ -3189,11 +3207,11 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont Assert(strcmp(strVal(lsecond(n->funcname)), "similar_to_escape") == 0); Assert(list_length(n->args) == 1 || list_length(n->args) == 2); - deparseExpr(str, linitial(n->args)); + deparseExpr(str, linitial(n->args), context); if (list_length(n->args) == 2) { appendStringInfoString(str, " ESCAPE "); - deparseExpr(str, lsecond(n->args)); + deparseExpr(str, lsecond(n->args), context); } return; @@ -3205,13 +3223,13 @@ static void deparseAExpr(StringInfo str, A_Expr* a_expr, DeparseNodeContext cont Assert(IsA(linitial(a_expr->name), String)); Assert(IsA(a_expr->rexpr, List)); - deparseExpr(str, a_expr->lexpr); + deparseExpr(str, a_expr->lexpr, context); appendStringInfoChar(str, ' '); appendStringInfoString(str, strVal(linitial(a_expr->name))); appendStringInfoChar(str, ' '); foreach(lc, castNode(List, a_expr->rexpr)) { - deparseExpr(str, lfirst(lc)); + deparseExpr(str, lfirst(lc), context); if (lnext(castNode(List, a_expr->rexpr), lc)) appendStringInfoString(str, " AND "); } @@ -3233,7 +3251,7 @@ static void deparseBoolExpr(StringInfo str, BoolExpr *bool_expr) if (need_parens) appendStringInfoChar(str, '('); - deparseExpr(str, lfirst(lc)); + deparseExpr(str, lfirst(lc), DEPARSE_NODE_CONTEXT_A_EXPR); if (need_parens) appendStringInfoChar(str, ')'); @@ -3251,7 +3269,7 @@ static void deparseBoolExpr(StringInfo str, BoolExpr *bool_expr) if (need_parens) appendStringInfoChar(str, '('); - deparseExpr(str, lfirst(lc)); + deparseExpr(str, lfirst(lc), DEPARSE_NODE_CONTEXT_A_EXPR); if (need_parens) appendStringInfoChar(str, ')'); @@ -3266,7 +3284,7 @@ static void deparseBoolExpr(StringInfo str, BoolExpr *bool_expr) appendStringInfoString(str, "NOT "); if (need_parens) appendStringInfoChar(str, '('); - deparseExpr(str, linitial(bool_expr->args)); + deparseExpr(str, linitial(bool_expr->args), DEPARSE_NODE_CONTEXT_A_EXPR); if (need_parens) appendStringInfoChar(str, ')'); return; @@ -3286,7 +3304,7 @@ static void deparseCollateClause(StringInfo str, CollateClause* collate_clause) bool need_parens = IsA(collate_clause->arg, A_Expr); if (need_parens) appendStringInfoChar(str, '('); - deparseExpr(str, collate_clause->arg); + deparseExpr(str, collate_clause->arg, DEPARSE_NODE_CONTEXT_A_EXPR); if (need_parens) appendStringInfoChar(str, ')'); appendStringInfoChar(str, ' '); @@ -3295,9 +3313,10 @@ static void deparseCollateClause(StringInfo str, CollateClause* collate_clause) deparseAnyName(str, collate_clause->collname); } +// "sortby" in gram.y static void deparseSortBy(StringInfo str, SortBy* sort_by) { - deparseExpr(str, sort_by->node); + deparseExpr(str, sort_by->node, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); switch (sort_by->sortby_dir) @@ -3414,6 +3433,7 @@ static void deparseWithClause(StringInfo str, WithClause *with_clause) removeTrailingSpace(str); } +// "joined_table" in gram.y static void deparseJoinExpr(StringInfo str, JoinExpr *join_expr) { ListCell *lc; @@ -3467,7 +3487,7 @@ static void deparseJoinExpr(StringInfo str, JoinExpr *join_expr) if (join_expr->quals != NULL) { appendStringInfoString(str, "ON "); - deparseExpr(str, join_expr->quals); + deparseExpr(str, join_expr->quals, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } @@ -3510,6 +3530,7 @@ static void deparseCTESearchClause(StringInfo str, CTESearchClause *search_claus appendStringInfoString(str, quote_identifier(search_clause->search_seq_column)); } +// "opt_cycle_clause" in gram.y static void deparseCTECycleClause(StringInfo str, CTECycleClause *cycle_clause) { appendStringInfoString(str, " CYCLE "); @@ -3523,15 +3544,15 @@ static void deparseCTECycleClause(StringInfo str, CTECycleClause *cycle_clause) if (cycle_clause->cycle_mark_value) { appendStringInfoString(str, " TO "); - deparseExpr(str, cycle_clause->cycle_mark_value); + deparseAexprConst(str, cycle_clause->cycle_mark_value); } - + if (cycle_clause->cycle_mark_default) { appendStringInfoString(str, " DEFAULT "); - deparseExpr(str, cycle_clause->cycle_mark_default); + deparseAexprConst(str, cycle_clause->cycle_mark_default); } - + appendStringInfoString(str, " USING "); appendStringInfoString(str, quote_identifier(cycle_clause->cycle_path_column)); } @@ -3696,7 +3717,7 @@ static void deparseTypeCast(StringInfo str, TypeCast *type_cast, DeparseNodeCont if (IsA(type_cast->arg, A_Expr) || context == DEPARSE_NODE_CONTEXT_FUNC_EXPR) { appendStringInfoString(str, "CAST("); - deparseExpr(str, type_cast->arg); + deparseExpr(str, type_cast->arg, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " AS "); deparseTypeName(str, type_cast->typeName); appendStringInfoChar(str, ')'); @@ -3763,7 +3784,7 @@ static void deparseTypeCast(StringInfo str, TypeCast *type_cast, DeparseNodeCont if (need_parens) appendStringInfoChar(str, '('); - deparseExpr(str, type_cast->arg); + deparseExpr(str, type_cast->arg, DEPARSE_NODE_CONTEXT_NONE /* could be either a_expr or b_expr (we could pass this down, but that'd require two kinds of contexts most likely) */); if (need_parens) appendStringInfoChar(str, ')'); @@ -3992,7 +4013,7 @@ static void deparseNullTest(StringInfo str, NullTest *null_test) // argisrow is always false in raw parser output Assert(null_test->argisrow == false); - deparseExpr(str, (Node *) null_test->arg); + deparseExpr(str, (Node *) null_test->arg, DEPARSE_NODE_CONTEXT_A_EXPR); switch (null_test->nulltesttype) { case IS_NULL: @@ -4004,6 +4025,7 @@ static void deparseNullTest(StringInfo str, NullTest *null_test) } } +// "case_expr" in gram.y static void deparseCaseExpr(StringInfo str, CaseExpr *case_expr) { ListCell *lc; @@ -4012,7 +4034,7 @@ static void deparseCaseExpr(StringInfo str, CaseExpr *case_expr) if (case_expr->arg != NULL) { - deparseExpr(str, (Node *) case_expr->arg); + deparseExpr(str, (Node *) case_expr->arg, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } @@ -4025,19 +4047,20 @@ static void deparseCaseExpr(StringInfo str, CaseExpr *case_expr) if (case_expr->defresult != NULL) { appendStringInfoString(str, "ELSE "); - deparseExpr(str, (Node *) case_expr->defresult); + deparseExpr(str, (Node *) case_expr->defresult, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } appendStringInfoString(str, "END"); } +// "when_clause" in gram.y static void deparseCaseWhen(StringInfo str, CaseWhen *case_when) { appendStringInfoString(str, "WHEN "); - deparseExpr(str, (Node *) case_when->expr); + deparseExpr(str, (Node *) case_when->expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " THEN "); - deparseExpr(str, (Node *) case_when->result); + deparseExpr(str, (Node *) case_when->result, DEPARSE_NODE_CONTEXT_A_EXPR); } static void deparseAIndirection(StringInfo str, A_Indirection *a_indirection) @@ -4055,7 +4078,7 @@ static void deparseAIndirection(StringInfo str, A_Indirection *a_indirection) if (need_parens) appendStringInfoChar(str, '('); - deparseExpr(str, a_indirection->arg); + deparseExpr(str, a_indirection->arg, need_parens ? DEPARSE_NODE_CONTEXT_A_EXPR : DEPARSE_NODE_CONTEXT_NONE); if (need_parens) appendStringInfoChar(str, ')'); @@ -4067,11 +4090,11 @@ static void deparseAIndices(StringInfo str, A_Indices *a_indices) { appendStringInfoChar(str, '['); if (a_indices->lidx != NULL) - deparseExpr(str, a_indices->lidx); + deparseExpr(str, a_indices->lidx, DEPARSE_NODE_CONTEXT_A_EXPR); if (a_indices->is_slice) appendStringInfoChar(str, ':'); if (a_indices->uidx != NULL) - deparseExpr(str, a_indices->uidx); + deparseExpr(str, a_indices->uidx, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ']'); } @@ -4104,7 +4127,7 @@ static void deparseBooleanTest(StringInfo str, BooleanTest *boolean_test) if (need_parens) appendStringInfoChar(str, '('); - deparseExpr(str, (Node *) boolean_test->arg); + deparseExpr(str, (Node *) boolean_test->arg, DEPARSE_NODE_CONTEXT_A_EXPR); if (need_parens) appendStringInfoChar(str, ')'); @@ -4134,6 +4157,7 @@ static void deparseBooleanTest(StringInfo str, BooleanTest *boolean_test) } } +// "columnDef" and "alter_table_cmd" in gram.y static void deparseColumnDef(StringInfo str, ColumnDef *column_def) { ListCell *lc; @@ -4160,7 +4184,7 @@ static void deparseColumnDef(StringInfo str, ColumnDef *column_def) if (column_def->raw_default != NULL) { appendStringInfoString(str, "USING "); - deparseExpr(str, column_def->raw_default); + deparseExpr(str, column_def->raw_default, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } @@ -4356,6 +4380,7 @@ static void deparseUpdateStmt(StringInfo str, UpdateStmt *update_stmt) removeTrailingSpace(str); } +// "MergeStmt" in gram.y static void deparseMergeStmt(StringInfo str, MergeStmt *merge_stmt) { if (merge_stmt->withClause != NULL) @@ -4373,7 +4398,7 @@ static void deparseMergeStmt(StringInfo str, MergeStmt *merge_stmt) appendStringInfoChar(str, ' '); appendStringInfoString(str, "ON "); - deparseExpr(str, merge_stmt->joinCondition); + deparseExpr(str, merge_stmt->joinCondition, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); ListCell *lc; @@ -4399,7 +4424,7 @@ static void deparseMergeStmt(StringInfo str, MergeStmt *merge_stmt) if (clause->condition) { appendStringInfoString(str, "AND "); - deparseExpr(str, clause->condition); + deparseExpr(str, clause->condition, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } @@ -4775,6 +4800,7 @@ static void deparseCreateExtensionStmt(StringInfo str, CreateExtensionStmt *crea removeTrailingSpace(str); } +// "ColConstraintElem" and "ConstraintElem" in gram.y static void deparseConstraint(StringInfo str, Constraint *constraint) { ListCell *lc; @@ -4816,12 +4842,12 @@ static void deparseConstraint(StringInfo str, Constraint *constraint) case CONSTR_GENERATED: Assert(constraint->generated_when == ATTRIBUTE_IDENTITY_ALWAYS); appendStringInfoString(str, "GENERATED ALWAYS AS ("); - deparseExpr(str, constraint->raw_expr); + deparseExpr(str, constraint->raw_expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") STORED "); break; case CONSTR_CHECK: appendStringInfoString(str, "CHECK ("); - deparseExpr(str, constraint->raw_expr); + deparseExpr(str, constraint->raw_expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); break; case CONSTR_PRIMARY: @@ -4855,7 +4881,7 @@ static void deparseConstraint(StringInfo str, Constraint *constraint) if (constraint->where_clause != NULL) { appendStringInfoString(str, "WHERE ("); - deparseExpr(str, constraint->where_clause); + deparseExpr(str, constraint->where_clause, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } break; @@ -5027,10 +5053,11 @@ static void deparseConstraint(StringInfo str, Constraint *constraint) removeTrailingSpace(str); } +// "ReturnStmt" in gram.y static void deparseReturnStmt(StringInfo str, ReturnStmt *return_stmt) { appendStringInfoString(str, "RETURN "); - deparseExpr(str, return_stmt->returnval); + deparseExpr(str, return_stmt->returnval, DEPARSE_NODE_CONTEXT_A_EXPR); } static void deparseCreateFunctionStmt(StringInfo str, CreateFunctionStmt *create_function_stmt) @@ -5129,6 +5156,7 @@ static void deparseCreateFunctionStmt(StringInfo str, CreateFunctionStmt *create removeTrailingSpace(str); } +// "func_arg", "func_arg_with_default" and other places in gram.y static void deparseFunctionParameter(StringInfo str, FunctionParameter *function_parameter) { switch (function_parameter->mode) @@ -5169,7 +5197,7 @@ static void deparseFunctionParameter(StringInfo str, FunctionParameter *function if (function_parameter->defexpr != NULL) { appendStringInfoString(str, "= "); - deparseExpr(str, function_parameter->defexpr); + deparseExpr(str, function_parameter->defexpr, DEPARSE_NODE_CONTEXT_A_EXPR); } removeTrailingSpace(str); @@ -5290,7 +5318,7 @@ static void deparsePartitionElem(StringInfo str, PartitionElem *partition_elem) else if (partition_elem->expr != NULL) { appendStringInfoChar(str, '('); - deparseExpr(str, partition_elem->expr); + deparseExpr(str, partition_elem->expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } @@ -6352,6 +6380,7 @@ static void deparseAlterObjectSchemaStmt(StringInfo str, AlterObjectSchemaStmt * appendStringInfoString(str, quote_identifier(alter_object_schema_stmt->newschema)); } +// "alter_table_cmd" in gram.y static void deparseAlterTableCmd(StringInfo str, AlterTableCmd *alter_table_cmd, DeparseNodeContext context) { ListCell *lc = NULL; @@ -6643,7 +6672,7 @@ static void deparseAlterTableCmd(StringInfo str, AlterTableCmd *alter_table_cmd, case AT_ColumnDefault: if (alter_table_cmd->def != NULL) { - deparseExpr(str, alter_table_cmd->def); + deparseExpr(str, alter_table_cmd->def, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); } break; @@ -6699,7 +6728,7 @@ static void deparseAlterTableCmd(StringInfo str, AlterTableCmd *alter_table_cmd, break; case AT_SetExpression: appendStringInfoString(str, "SET EXPRESSION AS ("); - deparseExpr(str, alter_table_cmd->def); + deparseExpr(str, alter_table_cmd->def, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); break; default: @@ -6807,6 +6836,7 @@ static void deparseAlterTableSpaceOptionsStmt(StringInfo str, AlterTableSpaceOpt deparseRelOptions(str, alter_table_space_options_stmt->options); } +// "AlterDomainStmt" in gram.y static void deparseAlterDomainStmt(StringInfo str, AlterDomainStmt *alter_domain_stmt) { appendStringInfoString(str, "ALTER DOMAIN "); @@ -6819,7 +6849,7 @@ static void deparseAlterDomainStmt(StringInfo str, AlterDomainStmt *alter_domain if (alter_domain_stmt->def != NULL) { appendStringInfoString(str, "SET DEFAULT "); - deparseExpr(str, alter_domain_stmt->def); + deparseExpr(str, alter_domain_stmt->def, DEPARSE_NODE_CONTEXT_A_EXPR); } else { @@ -9045,6 +9075,7 @@ static void deparseReplicaIdentityStmt(StringInfo str, ReplicaIdentityStmt *repl } } +// "CreatePolicyStmt" in gram.y static void deparseCreatePolicyStmt(StringInfo str, CreatePolicyStmt *create_policy_stmt) { ListCell *lc = NULL; @@ -9078,18 +9109,19 @@ static void deparseCreatePolicyStmt(StringInfo str, CreatePolicyStmt *create_pol if (create_policy_stmt->qual != NULL) { appendStringInfoString(str, "USING ("); - deparseExpr(str, create_policy_stmt->qual); + deparseExpr(str, create_policy_stmt->qual, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } if (create_policy_stmt->with_check != NULL) { appendStringInfoString(str, "WITH CHECK ("); - deparseExpr(str, create_policy_stmt->with_check); + deparseExpr(str, create_policy_stmt->with_check, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } } +// "AlterPolicyStmt" in gram.y static void deparseAlterPolicyStmt(StringInfo str, AlterPolicyStmt *alter_policy_stmt) { appendStringInfoString(str, "ALTER POLICY "); @@ -9108,14 +9140,14 @@ static void deparseAlterPolicyStmt(StringInfo str, AlterPolicyStmt *alter_policy if (alter_policy_stmt->qual != NULL) { appendStringInfoString(str, "USING ("); - deparseExpr(str, alter_policy_stmt->qual); + deparseExpr(str, alter_policy_stmt->qual, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } if (alter_policy_stmt->with_check != NULL) { appendStringInfoString(str, "WITH CHECK ("); - deparseExpr(str, alter_policy_stmt->with_check); + deparseExpr(str, alter_policy_stmt->with_check, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } } @@ -9202,6 +9234,7 @@ static void deparseCreateAmStmt(StringInfo str, CreateAmStmt *create_am_stmt) deparseHandlerName(str, create_am_stmt->handler_name); } +// "pub_obj_list" in gram.y static void deparsePublicationObjectList(StringInfo str, List *pubobjects) { const ListCell *lc; foreach(lc, pubobjects) { @@ -9222,7 +9255,7 @@ static void deparsePublicationObjectList(StringInfo str, List *pubobjects) { if (obj->pubtable->whereClause) { appendStringInfoString(str, " WHERE ("); - deparseExpr(str, obj->pubtable->whereClause); + deparseExpr(str, obj->pubtable->whereClause, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ")"); } @@ -9571,6 +9604,7 @@ static void deparseCommentStmt(StringInfo str, CommentStmt *comment_stmt) appendStringInfoString(str, "NULL"); } +// "stats_param" in gram.y static void deparseStatsElem(StringInfo str, StatsElem *stats_elem) { // only one of stats_elem->name or stats_elem->expr can be non-null @@ -9579,7 +9613,7 @@ static void deparseStatsElem(StringInfo str, StatsElem *stats_elem) else if (stats_elem->expr) { appendStringInfoChar(str, '('); - deparseExpr(str, stats_elem->expr); + deparseExpr(str, stats_elem->expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); } } @@ -9727,6 +9761,7 @@ static void deparseVariableShowStmt(StringInfo str, VariableShowStmt *variable_s appendStringInfoString(str, quote_identifier(variable_show_stmt->name)); } +// "tablesample_clause" in gram.y static void deparseRangeTableSample(StringInfo str, RangeTableSample *range_table_sample) { deparseRangeVar(str, castNode(RangeVar, range_table_sample->relation), DEPARSE_NODE_CONTEXT_NONE); @@ -9741,7 +9776,7 @@ static void deparseRangeTableSample(StringInfo str, RangeTableSample *range_tabl if (range_table_sample->repeatable != NULL) { appendStringInfoString(str, "REPEATABLE ("); - deparseExpr(str, range_table_sample->repeatable); + deparseExpr(str, range_table_sample->repeatable, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } @@ -9866,7 +9901,7 @@ static void deparseDropSubscriptionStmt(StringInfo str, DropSubscriptionStmt *dr static void deparseCallStmt(StringInfo str, CallStmt *call_stmt) { appendStringInfoString(str, "CALL "); - deparseFuncCall(str, call_stmt->funccall); + deparseFuncCall(str, call_stmt->funccall, DEPARSE_NODE_CONTEXT_NONE); } static void deparseAlterOwnerStmt(StringInfo str, AlterOwnerStmt *alter_owner_stmt) @@ -10058,6 +10093,7 @@ static void deparseClosePortalStmt(StringInfo str, ClosePortalStmt *close_portal } } +// "CreateTrigStmt" in gram.y static void deparseCreateTrigStmt(StringInfo str, CreateTrigStmt *create_trig_stmt) { ListCell *lc; @@ -10153,7 +10189,7 @@ static void deparseCreateTrigStmt(StringInfo str, CreateTrigStmt *create_trig_st if (create_trig_stmt->whenClause) { appendStringInfoString(str, "WHEN ("); - deparseExpr(str, create_trig_stmt->whenClause); + deparseExpr(str, create_trig_stmt->whenClause, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } @@ -10184,7 +10220,7 @@ static void deparseTriggerTransition(StringInfo str, TriggerTransition *trigger_ appendStringInfoString(str, quote_identifier(trigger_transition->name)); } -static void deparseXmlExpr(StringInfo str, XmlExpr* xml_expr) +static void deparseXmlExpr(StringInfo str, XmlExpr* xml_expr, DeparseNodeContext context) { switch (xml_expr->op) { @@ -10228,7 +10264,7 @@ static void deparseXmlExpr(StringInfo str, XmlExpr* xml_expr) default: Assert(false); } - deparseExpr(str, linitial(xml_expr->args)); + deparseExpr(str, linitial(xml_expr->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ')'); break; case IS_XMLPI: /* XMLPI(name [, args]) */ @@ -10237,18 +10273,18 @@ static void deparseXmlExpr(StringInfo str, XmlExpr* xml_expr) if (xml_expr->args != NULL) { appendStringInfoString(str, ", "); - deparseExpr(str, linitial(xml_expr->args)); + deparseExpr(str, linitial(xml_expr->args), DEPARSE_NODE_CONTEXT_A_EXPR); } appendStringInfoChar(str, ')'); break; case IS_XMLROOT: /* XMLROOT(xml, version, standalone) */ appendStringInfoString(str, "xmlroot("); - deparseExpr(str, linitial(xml_expr->args)); + deparseExpr(str, linitial(xml_expr->args), DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ", version "); if (castNode(A_Const, lsecond(xml_expr->args))->isnull) appendStringInfoString(str, "NO VALUE"); else - deparseExpr(str, lsecond(xml_expr->args)); + deparseExpr(str, lsecond(xml_expr->args), DEPARSE_NODE_CONTEXT_A_EXPR); if (intVal(&castNode(A_Const, lthird(xml_expr->args))->val) == XML_STANDALONE_YES) appendStringInfoString(str, ", STANDALONE YES"); else if (intVal(&castNode(A_Const, lthird(xml_expr->args))->val) == XML_STANDALONE_NO) @@ -10263,12 +10299,13 @@ static void deparseXmlExpr(StringInfo str, XmlExpr* xml_expr) break; case IS_DOCUMENT: /* xmlval IS DOCUMENT */ Assert(list_length(xml_expr->args) == 1); - deparseExpr(str, linitial(xml_expr->args)); + deparseExpr(str, linitial(xml_expr->args), context); appendStringInfoString(str, " IS DOCUMENT"); break; } } +// "xmltable_column_el" in gram.y static void deparseRangeTableFuncCol(StringInfo str, RangeTableFuncCol* range_table_func_col) { appendStringInfoString(str, quote_identifier(range_table_func_col->colname)); @@ -10286,14 +10323,14 @@ static void deparseRangeTableFuncCol(StringInfo str, RangeTableFuncCol* range_ta if (range_table_func_col->colexpr) { appendStringInfoString(str, "PATH "); - deparseExpr(str, range_table_func_col->colexpr); + deparseExpr(str, range_table_func_col->colexpr, DEPARSE_NODE_CONTEXT_NONE /* b_expr */); appendStringInfoChar(str, ' '); } if (range_table_func_col->coldefexpr) { appendStringInfoString(str, "DEFAULT "); - deparseExpr(str, range_table_func_col->coldefexpr); + deparseExpr(str, range_table_func_col->coldefexpr, DEPARSE_NODE_CONTEXT_NONE /* b_expr */); appendStringInfoChar(str, ' '); } @@ -10304,6 +10341,7 @@ static void deparseRangeTableFuncCol(StringInfo str, RangeTableFuncCol* range_ta removeTrailingSpace(str); } +// "table_ref" and "xmltable" in gram.y static void deparseRangeTableFunc(StringInfo str, RangeTableFunc* range_table_func) { ListCell *lc; @@ -10320,11 +10358,11 @@ static void deparseRangeTableFunc(StringInfo str, RangeTableFunc* range_table_fu } appendStringInfoChar(str, '('); - deparseExpr(str, range_table_func->rowexpr); + deparseExpr(str, range_table_func->rowexpr, DEPARSE_NODE_CONTEXT_NONE /* c_expr */); appendStringInfoChar(str, ')'); appendStringInfoString(str, " PASSING "); - deparseExpr(str, range_table_func->docexpr); + deparseExpr(str, range_table_func->docexpr, DEPARSE_NODE_CONTEXT_NONE /* c_expr */); appendStringInfoString(str, " COLUMNS "); foreach(lc, range_table_func->columns) @@ -10359,7 +10397,7 @@ static void deparseXmlSerialize(StringInfo str, XmlSerialize *xml_serialize) default: Assert(false); } - deparseExpr(str, xml_serialize->expr); + deparseExpr(str, xml_serialize->expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, " AS "); deparseTypeName(str, xml_serialize->typeName); @@ -10396,7 +10434,7 @@ static void deparseJsonFormat(StringInfo str, JsonFormat *json_format) static void deparseJsonIsPredicate(StringInfo str, JsonIsPredicate *j) { - deparseExpr(str, j->expr); + deparseExpr(str, j->expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); deparseJsonFormat(str, castNode(JsonFormat, j->format)); @@ -10428,7 +10466,7 @@ static void deparseJsonIsPredicate(StringInfo str, JsonIsPredicate *j) // "json_value_expr" in gram.y static void deparseJsonValueExpr(StringInfo str, JsonValueExpr *json_value_expr) { - deparseExpr(str, (Node *) json_value_expr->raw_expr); + deparseExpr(str, (Node *) json_value_expr->raw_expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoChar(str, ' '); deparseJsonFormat(str, json_value_expr->format); } @@ -10450,7 +10488,7 @@ static void deparseJsonValueExprList(StringInfo str, List *exprs) // "json_name_and_value" in gram.y static void deparseJsonKeyValue(StringInfo str, JsonKeyValue *json_key_value) { - deparseExpr(str, (Node *) json_key_value->key); + deparseExpr(str, (Node *) json_key_value->key, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ": "); deparseJsonValueExpr(str, json_key_value->value); } @@ -10482,6 +10520,7 @@ static void deparseJsonOutput(StringInfo str, JsonOutput *json_output) deparseJsonFormat(str, json_output->returning->format); } +// "json_aggregate_func" and "func_expr" in gram.y static void deparseJsonObjectAgg(StringInfo str, JsonObjectAgg *json_object_agg) { Assert(json_object_agg->constructor != NULL); @@ -10503,7 +10542,7 @@ static void deparseJsonObjectAgg(StringInfo str, JsonObjectAgg *json_object_agg) if (json_object_agg->constructor->agg_filter) { appendStringInfoString(str, "FILTER (WHERE "); - deparseExpr(str, json_object_agg->constructor->agg_filter); + deparseExpr(str, json_object_agg->constructor->agg_filter, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } @@ -10520,6 +10559,7 @@ static void deparseJsonObjectAgg(StringInfo str, JsonObjectAgg *json_object_agg) removeTrailingSpace(str); } +// "json_aggregate_func" and "func_expr" in gram.y static void deparseJsonArrayAgg(StringInfo str, JsonArrayAgg *json_array_agg) { Assert(json_array_agg->constructor != NULL); @@ -10539,7 +10579,7 @@ static void deparseJsonArrayAgg(StringInfo str, JsonArrayAgg *json_array_agg) if (json_array_agg->constructor->agg_filter) { appendStringInfoString(str, "FILTER (WHERE "); - deparseExpr(str, json_array_agg->constructor->agg_filter); + deparseExpr(str, json_array_agg->constructor->agg_filter, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ") "); } @@ -10614,7 +10654,7 @@ static void deparseJsonParseExpr(StringInfo str, JsonParseExpr *json_parse_expr) static void deparseJsonScalarExpr(StringInfo str, JsonScalarExpr *json_scalar_expr) { appendStringInfoString(str, "JSON_SCALAR("); - deparseExpr(str, (Node*) json_scalar_expr->expr); + deparseExpr(str, (Node*) json_scalar_expr->expr, DEPARSE_NODE_CONTEXT_A_EXPR); appendStringInfoString(str, ")"); } @@ -10685,7 +10725,7 @@ static void deparseJsonFuncExpr(StringInfo str, JsonFuncExpr *json_func_expr) deparseJsonValueExpr(str, json_func_expr->context_item); appendStringInfoString(str, ", "); - deparseExpr(str, json_func_expr->pathspec); + deparseExpr(str, json_func_expr->pathspec, DEPARSE_NODE_CONTEXT_A_EXPR); if (json_func_expr->passing) appendStringInfoString(str, " PASSING "); @@ -10741,6 +10781,7 @@ static void deparseJsonTablePathSpec(StringInfo str, JsonTablePathSpec *json_tab } } +// "json_behavior" in gram.y static void deparseJsonBehavior(StringInfo str, JsonBehavior *json_behavior) { switch (json_behavior->btype) @@ -10768,7 +10809,7 @@ static void deparseJsonBehavior(StringInfo str, JsonBehavior *json_behavior) break; case JSON_BEHAVIOR_DEFAULT: appendStringInfoString(str, "DEFAULT "); - deparseExpr(str, (Node*) json_behavior->expr); + deparseExpr(str, (Node*) json_behavior->expr, DEPARSE_NODE_CONTEXT_A_EXPR); break; case JSON_BEHAVIOR_UNKNOWN: appendStringInfoString(str, "UNKNOWN"); diff --git a/parser/src_backend_parser_gram.c b/parser/src_backend_parser_gram.c index 0ad51730..926e8d7c 100644 --- a/parser/src_backend_parser_gram.c +++ b/parser/src_backend_parser_gram.c @@ -34303,7 +34303,7 @@ YYLTYPE yylloc; n->def = (Node *) c; c->contype = CONSTR_FOREIGN; /* others not supported, yet */ c->conname = (yyvsp[(3) - (4)].str); - processCASbits((yyvsp[(4) - (4)].ival), (yylsp[(4) - (4)]), "ALTER CONSTRAINT statement", + processCASbits((yyvsp[(4) - (4)].ival), (yylsp[(4) - (4)]), "FOREIGN KEY", &c->deferrable, &c->initdeferred, NULL, NULL, yyscanner); diff --git a/parser/src_common_wchar.c b/parser/src_common_wchar.c index 9e6cb02b..1483b073 100644 --- a/parser/src_common_wchar.c +++ b/parser/src_common_wchar.c @@ -98,6 +98,25 @@ #include "utils/ascii.h" +/* + * In today's multibyte encodings other than UTF8, this two-byte sequence + * ensures pg_encoding_mblen() == 2 && pg_encoding_verifymbstr() == 0. + * + * For historical reasons, several verifychar implementations opt to reject + * this pair specifically. Byte pair range constraints, in encoding + * originator documentation, always excluded this pair. No core conversion + * could translate it. However, longstanding verifychar implementations + * accepted any non-NUL byte. big5_to_euc_tw and big5_to_mic even translate + * pairs not valid per encoding originator documentation. To avoid tightening + * core or non-core conversions in a security patch, we sought this one pair. + * + * PQescapeString() historically used spaces for BYTE1; many other values + * could suffice for BYTE1. + */ +#define NONUTF8_INVALID_BYTE0 (0x8d) +#define NONUTF8_INVALID_BYTE1 (' ') + + /* * Operations on multi-byte encodings are driven by a table of helper * functions. @@ -1547,6 +1566,11 @@ pg_big5_verifychar(const unsigned char *s, int len) if (len < l) return -1; + if (l == 2 && + s[0] == NONUTF8_INVALID_BYTE0 && + s[1] == NONUTF8_INVALID_BYTE1) + return -1; + while (--l > 0) { if (*++s == '\0') @@ -1596,6 +1620,11 @@ pg_gbk_verifychar(const unsigned char *s, int len) if (len < l) return -1; + if (l == 2 && + s[0] == NONUTF8_INVALID_BYTE0 && + s[1] == NONUTF8_INVALID_BYTE1) + return -1; + while (--l > 0) { if (*++s == '\0') @@ -1645,6 +1674,11 @@ pg_uhc_verifychar(const unsigned char *s, int len) if (len < l) return -1; + if (l == 2 && + s[0] == NONUTF8_INVALID_BYTE0 && + s[1] == NONUTF8_INVALID_BYTE1) + return -1; + while (--l > 0) { if (*++s == '\0') @@ -2089,6 +2123,12 @@ pg_utf8_islegal(const unsigned char *source, int length) } +/* + * Fills the provided buffer with two bytes such that: + * pg_encoding_mblen(dst) == 2 && pg_encoding_verifymbstr(dst) == 0 + */ + + /* *------------------------------------------------------------------- * encoding info table @@ -2188,5 +2228,11 @@ pg_encoding_max_length(int encoding) { Assert(PG_VALID_ENCODING(encoding)); - return pg_wchar_table[encoding].maxmblen; + /* + * Check for the encoding despite the assert, due to some mingw versions + * otherwise issuing bogus warnings. + */ + return PG_VALID_ENCODING(encoding) ? + pg_wchar_table[encoding].maxmblen : + pg_wchar_table[PG_SQL_ASCII].maxmblen; } diff --git a/parser/src_port_snprintf.c b/parser/src_port_snprintf.c index 4f2fbbbe..6d05feff 100644 --- a/parser/src_port_snprintf.c +++ b/parser/src_port_snprintf.c @@ -3,7 +3,7 @@ * - pg_vsnprintf * - dopr * - pg_snprintf - * - strchrnul + * - pg_strchrnul * - dostr * - flushbuffer * - find_arguments @@ -362,13 +362,22 @@ static void leading_pad(int zpad, int signvalue, int *padlen, static void trailing_pad(int padlen, PrintfTarget *target); /* - * If strchrnul exists (it's a glibc-ism), it's a good bit faster than the - * equivalent manual loop. If it doesn't exist, provide a replacement. + * If strchrnul exists (it's a glibc-ism, but since adopted by some other + * platforms), it's a good bit faster than the equivalent manual loop. + * Use it if possible, and if it doesn't exist, use this replacement. * * Note: glibc declares this as returning "char *", but that would require * casting away const internally, so we don't follow that detail. + * + * Note: macOS has this too as of Sequoia 15.4, but it's hidden behind + * a deployment-target check that causes compile errors if the deployment + * target isn't high enough. So !HAVE_DECL_STRCHRNUL may mean "yes it's + * declared, but it doesn't compile". To avoid failing in that scenario, + * use a macro to avoid matching 's name. */ -#ifndef HAVE_STRCHRNUL +#if !HAVE_DECL_STRCHRNUL + +#define strchrnul pg_strchrnul static inline const char * strchrnul(const char *s, int c) @@ -378,19 +387,7 @@ strchrnul(const char *s, int c) return s; } -#else - -/* - * glibc's declares strchrnul only if _GNU_SOURCE is defined. - * While we typically use that on glibc platforms, configure will set - * HAVE_STRCHRNUL whether it's used or not. Fill in the missing declaration - * so that this file will compile cleanly with or without _GNU_SOURCE. - */ -#ifndef _GNU_SOURCE -extern char *strchrnul(const char *s, int c); -#endif - -#endif /* HAVE_STRCHRNUL */ +#endif /* !HAVE_DECL_STRCHRNUL */ /* diff --git a/pg_query.pb.go b/pg_query.pb.go index 3ddd8f4c..480beb8d 100644 --- a/pg_query.pb.go +++ b/pg_query.pb.go @@ -3,7 +3,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v5.29.3 +// protoc v5.29.1 // source: pg_query.proto package pg_query