{
    "content": [
        {
            "type": "text",
            "text": "# CREATE_POLICY(7) (man)\n\n**Summary:** CREATEPOLICY - define a new row-level security policy for a table\n\n**Synopsis:** CREATE POLICY name ON tablename\n[ AS { PERMISSIVE | RESTRICTIVE } ]\n[ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]\n[ TO { rolename | PUBLIC | CURRENTROLE | CURRENTUSER | SESSIONUSER } [, ...] ]\n[ USING ( usingexpression ) ]\n[ WITH CHECK ( checkexpression ) ]\n\n## See Also\n\n- ALTERPOLICY(7)\n- DROPPOLICY(7)\n- ALTERTABLE(7)\n- POLICY(7)\n\n## Section Outline\n\n- **NAME** (2 lines)\n- **SYNOPSIS** (7 lines)\n- **DESCRIPTION** (34 lines)\n- **PARAMETERS** (52 lines) — 3 subsections\n  - Per-Command Policies (98 lines)\n  - Table 281. Policies Applied by Command Type (42 lines)\n  - Application of Multiple Policies (46 lines)\n- **NOTES** (32 lines)\n- **COMPATIBILITY** (2 lines)\n- **SEE ALSO** (5 lines)\n\n## Full Content\n\n### NAME\n\nCREATEPOLICY - define a new row-level security policy for a table\n\n### SYNOPSIS\n\nCREATE POLICY name ON tablename\n[ AS { PERMISSIVE | RESTRICTIVE } ]\n[ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]\n[ TO { rolename | PUBLIC | CURRENTROLE | CURRENTUSER | SESSIONUSER } [, ...] ]\n[ USING ( usingexpression ) ]\n[ WITH CHECK ( checkexpression ) ]\n\n### DESCRIPTION\n\nThe CREATE POLICY command defines a new row-level security policy for a table. Note that\nrow-level security must be enabled on the table (using ALTER TABLE ... ENABLE ROW LEVEL\nSECURITY) in order for created policies to be applied.\n\nA policy grants the permission to select, insert, update, or delete rows that match the\nrelevant policy expression. Existing table rows are checked against the expression specified\nin USING, while new rows that would be created via INSERT or UPDATE are checked against the\nexpression specified in WITH CHECK. When a USING expression returns true for a given row then\nthat row is visible to the user, while if false or null is returned then the row is not\nvisible. Typically, no error occurs when a row is not visible, but see Table 281 for\nexceptions. When a WITH CHECK expression returns true for a row then that row is inserted or\nupdated, while if false or null is returned then an error occurs.\n\nFor INSERT and UPDATE statements, WITH CHECK expressions are enforced after BEFORE triggers\nare fired, and before any actual data modifications are made. Thus a BEFORE ROW trigger may\nmodify the data to be inserted, affecting the result of the security policy check.  WITH\nCHECK expressions are enforced before any other constraints.\n\nPolicy names are per-table. Therefore, one policy name can be used for many different tables\nand have a definition for each table which is appropriate to that table.\n\nPolicies can be applied for specific commands or for specific roles. The default for newly\ncreated policies is that they apply for all commands and roles, unless otherwise specified.\nMultiple policies may apply to a single command; see below for more details.  Table 281\nsummarizes how the different types of policy apply to specific commands.\n\nFor policies that can have both USING and WITH CHECK expressions (ALL and UPDATE), if no WITH\nCHECK expression is defined, then the USING expression will be used both to determine which\nrows are visible (normal USING case) and which new rows will be allowed to be added (WITH\nCHECK case).\n\nIf row-level security is enabled for a table, but no applicable policies exist, a “default\ndeny” policy is assumed, so that no rows will be visible or updatable.\n\n### PARAMETERS\n\nname\nThe name of the policy to be created. This must be distinct from the name of any other\npolicy for the table.\n\ntablename\nThe name (optionally schema-qualified) of the table the policy applies to.\n\nPERMISSIVE\nSpecify that the policy is to be created as a permissive policy. All permissive policies\nwhich are applicable to a given query will be combined together using the Boolean “OR”\noperator. By creating permissive policies, administrators can add to the set of records\nwhich can be accessed. Policies are permissive by default.\n\nRESTRICTIVE\nSpecify that the policy is to be created as a restrictive policy. All restrictive\npolicies which are applicable to a given query will be combined together using the\nBoolean “AND” operator. By creating restrictive policies, administrators can reduce the\nset of records which can be accessed as all restrictive policies must be passed for each\nrecord.\n\nNote that there needs to be at least one permissive policy to grant access to records\nbefore restrictive policies can be usefully used to reduce that access. If only\nrestrictive policies exist, then no records will be accessible. When a mix of permissive\nand restrictive policies are present, a record is only accessible if at least one of the\npermissive policies passes, in addition to all the restrictive policies.\n\ncommand\nThe command to which the policy applies. Valid options are ALL, SELECT, INSERT, UPDATE,\nand DELETE.  ALL is the default. See below for specifics regarding how these are applied.\n\nrolename\nThe role(s) to which the policy is to be applied. The default is PUBLIC, which will apply\nthe policy to all roles.\n\nusingexpression\nAny SQL conditional expression (returning boolean). The conditional expression cannot\ncontain any aggregate or window functions. This expression will be added to queries that\nrefer to the table if row-level security is enabled. Rows for which the expression\nreturns true will be visible. Any rows for which the expression returns false or null\nwill not be visible to the user (in a SELECT), and will not be available for modification\n(in an UPDATE or DELETE). Typically, such rows are silently suppressed; no error is\nreported (but see Table 281 for exceptions).\n\ncheckexpression\nAny SQL conditional expression (returning boolean). The conditional expression cannot\ncontain any aggregate or window functions. This expression will be used in INSERT and\nUPDATE queries against the table if row-level security is enabled. Only rows for which\nthe expression evaluates to true will be allowed. An error will be thrown if the\nexpression evaluates to false or null for any of the records inserted or any of the\nrecords that result from the update. Note that the checkexpression is evaluated against\nthe proposed new contents of the row, not the original contents.\n\n#### Per-Command Policies\n\nALL\nUsing ALL for a policy means that it will apply to all commands, regardless of the type\nof command. If an ALL policy exists and more specific policies exist, then both the ALL\npolicy and the more specific policy (or policies) will be applied. Additionally, ALL\npolicies will be applied to both the selection side of a query and the modification side,\nusing the USING expression for both cases if only a USING expression has been defined.\n\nAs an example, if an UPDATE is issued, then the ALL policy will be applicable both to\nwhat the UPDATE will be able to select as rows to be updated (applying the USING\nexpression), and to the resulting updated rows, to check if they are permitted to be\nadded to the table (applying the WITH CHECK expression, if defined, and the USING\nexpression otherwise). If an INSERT or UPDATE command attempts to add rows to the table\nthat do not pass the ALL policy's WITH CHECK expression (or its USING expression, if it\ndoes not have a WITH CHECK expression), the entire command will be aborted.\n\nSELECT\nUsing SELECT for a policy means that it will apply to SELECT queries and whenever SELECT\npermissions are required on the relation the policy is defined for. The result is that\nonly those records from the relation that pass the SELECT policy will be returned during\na SELECT query, and that queries that require SELECT permissions, such as UPDATE and\nDELETE, will also only see those records that are allowed by the SELECT policy. A SELECT\npolicy cannot have a WITH CHECK expression, as it only applies in cases where records are\nbeing retrieved from the relation, except as described below.\n\nIf a data-modifying query has a RETURNING clause, SELECT permissions are required on the\nrelation, and any newly inserted or updated rows from the relation must satisfy the\nrelation's SELECT policies in order to be available to the RETURNING clause. If a newly\ninserted or updated row does not satisfy the relation's SELECT policies, an error will be\nthrown (inserted or updated rows to be returned are never silently ignored).\n\nIf an INSERT has an ON CONFLICT DO UPDATE clause, or an ON CONFLICT DO NOTHING clause\nwith an arbiter index or constraint specification, then SELECT permissions are required\non the relation, and the rows proposed for insertion are checked using the relation's\nSELECT policies. If a row proposed for insertion does not satisfy the relation's SELECT\npolicies, an error is thrown (the INSERT is never silently avoided). In addition, if the\nUPDATE path is taken, the row to be updated and the new updated row are checked against\nthe relation's SELECT policies, and an error is thrown if they are not satisfied (an\nauxiliary UPDATE is never silently avoided).\n\nINSERT\nUsing INSERT for a policy means that it will apply to INSERT commands. Rows being\ninserted that do not pass this policy will result in a policy violation error, and the\nentire INSERT command will be aborted. An INSERT policy cannot have a USING expression,\nas it only applies in cases where records are being added to the relation.\n\nNote that an INSERT with an ON CONFLICT DO NOTHING/UPDATE clause will check the INSERT\npolicies' WITH CHECK expressions for all rows proposed for insertion, regardless of\nwhether or not they end up being inserted.\n\nUPDATE\nUsing UPDATE for a policy means that it will apply to UPDATE, SELECT FOR UPDATE and\nSELECT FOR SHARE commands, as well as auxiliary ON CONFLICT DO UPDATE clauses of INSERT\ncommands. Since UPDATE involves pulling an existing record and replacing it with a new\nmodified record, UPDATE policies accept both a USING expression and a WITH CHECK\nexpression. The USING expression determines which records the UPDATE command will see to\noperate against, while the WITH CHECK expression defines which modified rows are allowed\nto be stored back into the relation.\n\nAny rows whose updated values do not pass the WITH CHECK expression will cause an error,\nand the entire command will be aborted. If only a USING clause is specified, then that\nclause will be used for both USING and WITH CHECK cases.\n\nTypically an UPDATE command also needs to read data from columns in the relation being\nupdated (e.g., in a WHERE clause or a RETURNING clause, or in an expression on the right\nhand side of the SET clause). In this case, SELECT rights are also required on the\nrelation being updated, and the appropriate SELECT or ALL policies will be applied in\naddition to the UPDATE policies. Thus the user must have access to the row(s) being\nupdated through a SELECT or ALL policy in addition to being granted permission to update\nthe row(s) via an UPDATE or ALL policy.\n\nWhen an INSERT command has an auxiliary ON CONFLICT DO UPDATE clause, if the UPDATE path\nis taken, the row to be updated is first checked against the USING expressions of any\nUPDATE policies, and then the new updated row is checked against the WITH CHECK\nexpressions. Note, however, that unlike a standalone UPDATE command, if the existing row\ndoes not pass the USING expressions, an error will be thrown (the UPDATE path will never\nbe silently avoided).\n\nDELETE\nUsing DELETE for a policy means that it will apply to DELETE commands. Only rows that\npass this policy will be seen by a DELETE command. There can be rows that are visible\nthrough a SELECT policy that are not available for deletion, if they do not pass the\nUSING expression for the DELETE policy.\n\nIn most cases a DELETE command also needs to read data from columns in the relation that\nit is deleting from (e.g., in a WHERE clause or a RETURNING clause). In this case, SELECT\nrights are also required on the relation, and the appropriate SELECT or ALL policies will\nbe applied in addition to the DELETE policies. Thus the user must have access to the\nrow(s) being deleted through a SELECT or ALL policy in addition to being granted\npermission to delete the row(s) via a DELETE or ALL policy.\n\nA DELETE policy cannot have a WITH CHECK expression, as it only applies in cases where\nrecords are being deleted from the relation, so that there is no new row to check.\n\nTable 281 summarizes how the different types of policy apply to specific commands. In the\ntable, “check” means that the policy expression is checked and an error is thrown if it\nreturns false or null, whereas “filter” means that the row is silently ignored if the policy\nexpression returns false or null.\n\n#### Table 281. Policies Applied by Command Type\n\n┌───────────────┬────────────────┬───────────────┬────────────────────────────────┬──────────────┐\n│               │ SELECT/ALL     │ INSERT/ALL    │ UPDATE/ALL policy              │ DELETE/ALL   │\n│               │ policy         │ policy        │                                │ policy       │\n│Command        ├────────────────┼───────────────┼────────────────┬───────────────┼──────────────┤\n│               │ USING          │ WITH CHECK    │ USING          │ WITH CHECK    │ USING        │\n│               │ expression     │ expression    │ expression     │ expression    │ expression   │\n├───────────────┼────────────────┼───────────────┼────────────────┼───────────────┼──────────────┤\n│SELECT / COPY  │ Filter         │ —             │ —              │ —             │ —            │\n│... TO         │ existing row   │               │                │               │              │\n├───────────────┼────────────────┼───────────────┼────────────────┼───────────────┼──────────────┤\n│SELECT FOR     │ Filter         │ —             │ Filter         │ —             │ —            │\n│UPDATE/SHARE   │ existing row   │               │ existing row   │               │              │\n├───────────────┼────────────────┼───────────────┼────────────────┼───────────────┼──────────────┤\n│INSERT         │ Check new      │ Check new row │ —              │ —             │ —            │\n│               │ row [a]        │               │                │               │              │\n├───────────────┼────────────────┼───────────────┼────────────────┼───────────────┼──────────────┤\n│UPDATE         │ Filter         │ —             │ Filter         │ Check new row │ —            │\n│               │ existing       │               │ existing row   │               │              │\n│               │ row [a] &      │               │                │               │              │\n│               │         check  │               │                │               │              │\n│               │ new row [a]    │               │                │               │              │\n├───────────────┼────────────────┼───────────────┼────────────────┼───────────────┼──────────────┤\n│DELETE         │ Filter         │ —             │ —              │ —             │ Filter       │\n│               │ existing       │               │                │               │ existing row │\n│               │ row [a]        │               │                │               │              │\n├───────────────┼────────────────┼───────────────┼────────────────┼───────────────┼──────────────┤\n│INSERT ... ON  │ Check new      │ Check new     │ —              │ —             │ —            │\n│CONFLICT       │ row [b][c]     │ row [c]       │                │               │              │\n├───────────────┼────────────────┼───────────────┼────────────────┼───────────────┼──────────────┤\n│ON CONFLICT DO │ Check existing │ —             │ Check existing │ Check new     │ —            │\n│UPDATE         │ & new rows [d] │               │ row            │ row [d]       │              │\n├───────────────┴────────────────┴───────────────┴────────────────┴───────────────┴──────────────┤\n│----                                                                                            │\n│[a] If read access is required to either the existing or new row (for example, a WHERE          │\n│or RETURNING clause that refers to columns from the relation).  ----                            │\n│[b] If an arbiter index or constraint is specified.  ----                                       │\n│[c] Row proposed for insertion is checked regardless of whether or not a conflict               │\n│occurs.  ----                                                                                   │\n│[d] New row of the auxiliary UPDATE command, which might be different from the new row          │\n│of the original INSERT command.                                                                 │\n└────────────────────────────────────────────────────────────────────────────────────────────────┘\n\n#### Application of Multiple Policies\n\nWhen multiple policies of different command types apply to the same command (for example,\nSELECT and UPDATE policies applied to an UPDATE command), then the user must have both types\nof permissions (for example, permission to select rows from the relation as well as\npermission to update them). Thus the expressions for one type of policy are combined with the\nexpressions for the other type of policy using the AND operator.\n\nWhen multiple policies of the same command type apply to the same command, then there must be\nat least one PERMISSIVE policy granting access to the relation, and all of the RESTRICTIVE\npolicies must pass. Thus all the PERMISSIVE policy expressions are combined using OR, all the\nRESTRICTIVE policy expressions are combined using AND, and the results are combined using\nAND. If there are no PERMISSIVE policies, then access is denied.\n\nNote that, for the purposes of combining multiple policies, ALL policies are treated as\nhaving the same type as whichever other type of policy is being applied.\n\nFor example, in an UPDATE command requiring both SELECT and UPDATE permissions, if there are\nmultiple applicable policies of each type, they will be combined as follows:\n\nexpression from RESTRICTIVE SELECT/ALL policy 1\nAND\nexpression from RESTRICTIVE SELECT/ALL policy 2\nAND\n...\nAND\n(\nexpression from PERMISSIVE SELECT/ALL policy 1\nOR\nexpression from PERMISSIVE SELECT/ALL policy 2\nOR\n...\n)\nAND\nexpression from RESTRICTIVE UPDATE/ALL policy 1\nAND\nexpression from RESTRICTIVE UPDATE/ALL policy 2\nAND\n...\nAND\n(\nexpression from PERMISSIVE UPDATE/ALL policy 1\nOR\nexpression from PERMISSIVE UPDATE/ALL policy 2\nOR\n...\n)\n\n### NOTES\n\nYou must be the owner of a table to create or change policies for it.\n\nWhile policies will be applied for explicit queries against tables in the database, they are\nnot applied when the system is performing internal referential integrity checks or validating\nconstraints. This means there are indirect ways to determine that a given value exists. An\nexample of this is attempting to insert a duplicate value into a column that is a primary key\nor has a unique constraint. If the insert fails then the user can infer that the value\nalready exists. (This example assumes that the user is permitted by policy to insert records\nwhich they are not allowed to see.) Another example is where a user is allowed to insert into\na table which references another, otherwise hidden table. Existence can be determined by the\nuser inserting values into the referencing table, where success would indicate that the value\nexists in the referenced table. These issues can be addressed by carefully crafting policies\nto prevent users from being able to insert, delete, or update records at all which might\npossibly indicate a value they are not otherwise able to see, or by using generated values\n(e.g., surrogate keys) instead of keys with external meanings.\n\nGenerally, the system will enforce filter conditions imposed using security policies prior to\nqualifications that appear in user queries, in order to prevent inadvertent exposure of the\nprotected data to user-defined functions which might not be trustworthy. However, functions\nand operators marked by the system (or the system administrator) as LEAKPROOF may be\nevaluated before policy expressions, as they are assumed to be trustworthy.\n\nSince policy expressions are added to the user's query directly, they will be run with the\nrights of the user running the overall query. Therefore, users who are using a given policy\nmust be able to access any tables or functions referenced in the expression or they will\nsimply receive a permission denied error when attempting to query the table that has\nrow-level security enabled. This does not change how views work, however. As with normal\nqueries and views, permission checks and policies for the tables which are referenced by a\nview will use the view owner's rights and any policies which apply to the view owner.\n\nAdditional discussion and practical examples can be found in Section 5.8.\n\n### COMPATIBILITY\n\nCREATE POLICY is a PostgreSQL extension.\n\n### SEE ALSO\n\nALTER POLICY (ALTERPOLICY(7)), DROP POLICY (DROPPOLICY(7)), ALTER TABLE (ALTERTABLE(7))\n\n\n\nPostgreSQL 14.23                                2026                                CREATE POLICY(7)\n\n"
        }
    ],
    "structuredContent": {
        "command": "CREATE_POLICY",
        "section": "7",
        "mode": "man",
        "summary": "CREATEPOLICY - define a new row-level security policy for a table",
        "synopsis": "CREATE POLICY name ON tablename\n[ AS { PERMISSIVE | RESTRICTIVE } ]\n[ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]\n[ TO { rolename | PUBLIC | CURRENTROLE | CURRENTUSER | SESSIONUSER } [, ...] ]\n[ USING ( usingexpression ) ]\n[ WITH CHECK ( checkexpression ) ]",
        "flags": [],
        "examples": [],
        "see_also": [
            {
                "name": "ALTERPOLICY",
                "section": "7",
                "url": "https://www.chedong.com/phpMan.php/man/ALTERPOLICY/7/json"
            },
            {
                "name": "DROPPOLICY",
                "section": "7",
                "url": "https://www.chedong.com/phpMan.php/man/DROPPOLICY/7/json"
            },
            {
                "name": "ALTERTABLE",
                "section": "7",
                "url": "https://www.chedong.com/phpMan.php/man/ALTERTABLE/7/json"
            },
            {
                "name": "POLICY",
                "section": "7",
                "url": "https://www.chedong.com/phpMan.php/man/POLICY/7/json"
            }
        ],
        "section_outline": [
            {
                "name": "NAME",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "SYNOPSIS",
                "lines": 7,
                "subsections": []
            },
            {
                "name": "DESCRIPTION",
                "lines": 34,
                "subsections": []
            },
            {
                "name": "PARAMETERS",
                "lines": 52,
                "subsections": [
                    {
                        "name": "Per-Command Policies",
                        "lines": 98
                    },
                    {
                        "name": "Table 281. Policies Applied by Command Type",
                        "lines": 42
                    },
                    {
                        "name": "Application of Multiple Policies",
                        "lines": 46
                    }
                ]
            },
            {
                "name": "NOTES",
                "lines": 32,
                "subsections": []
            },
            {
                "name": "COMPATIBILITY",
                "lines": 2,
                "subsections": []
            },
            {
                "name": "SEE ALSO",
                "lines": 5,
                "subsections": []
            }
        ]
    }
}