<img height="1" width="1" style="display:none;" alt="" src="https://px.ads.linkedin.com/collect/?pid=2826169&amp;fmt=gif">
Start trial

    Start trial

      Data masking protects sensitive data without getting in the way of the teams that need it. Discover how DBAs can handle data-type challenges, apply short-term controls, and build a sustainable, role-based masking strategy.

       
      Learn how to align data masking policies with supported data types in Fujitsu Enterprise Postgres

      Why access control matters for sensitive customer data

      Recent events continue to highlight the importance of carefully controlling access to customer information. Recently an Australia Utility company announced that it was investigating a potential security incident involving unauthorized access to some customer data. According to the company's statement, the incident may have involved unauthorized access to customer information, demonstrating that even large organizations can face significant data security risks.

      This serves as a reminder that organizations should apply the principle of least privilege, ensuring users can access only the information required to perform their roles. By limiting access to customer data, businesses can reduce the potential impact of security incidents and better protect individuals' privacy.

      The following production-style DBA scenario illustrates the challenge:

      In a financial services company, an operations team requires limited access to sensitive customer records in Fujitsu Enterprise Postgres. While creating a Data Masking policy, the DBA discovers that the email column uses the unsupported text data type and the document column uses bytea, which is also outside the documented masking matrix. This article examines the challenge from both a short-term containment and long-term remediation perspective, demonstrating how immediate access controls can reduce exposure, while a maintainable Data Masking and role-based access model is established.

      A production-style DBA workflow

      How Data Masking makes the decision

      Data Masking changes values as they are returned from a query while leaving the stored source values unchanged. A policy identifies the table, the Boolean condition and the target columns. When the condition evaluates to true, Fujitsu Enterprise Postgres returns the masked representation; when it evaluates to false, the original value is returned.

      Only one masking policy can be created for a table, but several supported columns can be added to that policy. This makes the policy condition especially important: it becomes the shared decision point for every masking target in the table.

      How the default-deny role policy decides what a user sees

      The walkthrough uses a default-deny model: users receive masked values unless their login belongs to the authorized group role

      The masking methods at a glance

      Before applying a masking policy, it helps to understand how each masking method changes the values returned to different users.

      Method What it does Example input Example result
      Full Replaces the entire value with the configured masking value 5000.00 0
      Partial Preserves selected characters while masking others 012-3456-7890 012-****-****
      Regexp Transforms matching parts of a value using a regular expression name@example.com xxx@example.com

      Understanding masking requirements

      The email column was defined using the text data type, a common choice because email addresses vary in length. However, when data masking was applied, the column's data type became significant, because only specific data types are supported as masking targets.

      Create the demonstration schema and table

      To reproduce the scenario, create a dedicated schema and a customer table that contains a mixture of supported and unsupported masking targets. In this example, the email column uses the text data type and the id_document column uses bytea, allowing the masking limitations to be demonstrated in a controlled environment while preserving a realistic customer data model.

      postgres=# CREATE SCHEMA data_masking_demo;
      
      CREATE TABLE data_masking_demo.customer_sensitive
      (
          customer_id BIGINT PRIMARY KEY,
          full_name   TEXT,
      CREATE SCHEMA
      postgres=#
      postgres=# CREATE TABLE data_masking_demo.customer_sensitive
      postgres-# (
      postgres(#     customer_id BIGINT PRIMARY KEY,
      postgres(#     full_name   TEXT,
      postgres(#     email       TEXT,              -- Target used to reproduce the limitation
      postgres(#     phone       VARCHAR(13),
      postgres(#     salary      NUMERIC(12,2),
      postgres(#     created_at  TIMESTAMP,
      postgres(#     id_document BYTEA
      postgres(# );
      CREATE TABLE

      Insert representative data

      Insert a small set of representative customer records. The sample data includes personally identifiable information, salary data and binary document content stored as bytea. These values provide realistic masking candidates and allow the behavior of both supported and unsupported data types to be observed.

      postgres=# INSERT INTO data_masking_demo.customer_sensitive
      postgres-#     (customer_id, full_name, email, phone, salary, created_at, id_document)
      postgres-# VALUES
      postgres-#     (1, 'Diksha Sharma', 'diksha.sharma@example.com',
      postgres(#      '012-3456-7890', 5000.00, '2026-07-20 10:15:30',
      postgres(#      decode('89504E470D0A1A0A', 'hex')),
      postgres-#     (2, 'John Smith', 'john.smith@example.com',
      postgres(#      '012-3456-7891', 8000.00, '2026-07-20 11:20:45',
      postgres(#      decode('25504446', 'hex'));
      INSERT 0 2

      Attempt to mask the email address

      The next step is to create a confidential policy that applies REGEXP masking to the email column. Because the column is defined as text, which is not included in the documented masking matrix for this masking method, the policy creation is expected to fail. This confirms that the issue is related to data-type support rather than permissions, syntax or the policy definition itself.

      2026-07-24 14:56:28.341 AEST [1995572] STATEMENT:  SELECT pgx_create_confidential_policy(
          schema_name        := 'data_masking_demo',
          table_name         := 'customer_sensitive',
          policy_name        := 'customer_masking_policy',
          expression         := '1=1',
          enable             := true,
          column_name        := 'email',
          function_type      := 'REGEXP',
          regexp_pattern     := '(.*)@(.*)',
          regexp_replacement := 'xxx\2',
          regexp_flags       := 'g'
      );
      
      ERROR:  The specified argument is invalid.

      The turning point: The problem was the data type, not the pattern

      The regular expression was not the root cause of the failure. The masking matrix for Fujitsu Enterprise Postgres lists bounded character types such as varchar(n), character varying(n), char(n) and character(n) as supported targets for regular expression masking. The text and bytea data types are not included in the documented matrix. This distinction is important. A column may contain character data and still fall outside the documented masking matrix. Likewise, a binary column can contain sensitive information but require a different protection strategy because bytea is not a supported masking target. Understanding this distinction helped separate the immediate symptom from the actual cause and guided the selection of an appropriate long-term solution.

      For the list of data types supported by each data masking method, refer to the Fujitsu Enterprise Postgres Operation Guide > Chapter 8 - Data Masking > 8.3 - Data types for masking.

      Column type Masking position Practical response
      varchar(n), char(n) Inside documented matrix Use FULL, PARTIAL or REGEXP as supported.
      numeric, date, timestamp Inside documented matrix Use the masking types documented for that category.
      text, bytea Outside documented matrix Use privileges, a protected view, a separate table or another approved design.

      The diagram below illustrates the journey from policy failure to a supported design. Initially, the masking policy is rejected because the email column uses the TEXT data type. A temporary protected view reduces exposure while the root cause is investigated. The email column is then converted to varchar(320), allowing the masking policy to be applied successfully and integrated with role-based access controls.

      Resolving the situation

      Addressing the issue involved two stages. First, access to the original values needed to be restricted while the production design was reviewed. Second, a long-term protection model had to be selected and implemented. This walkthrough demonstrates one practical approach rather than every possible solution.

      Short-term response: Contain access while design is reviewed

      Before changing a production column, administrators should review factors such as existing data, indexes, constraints, application dependencies, replication requirements and operational processes. Until that assessment is complete, access to the original values should be limited wherever possible.

      In this example, a temporary containment strategy is used. Direct access to the table is removed and a security-barrier view provides a redacted representation of the sensitive data. This approach reduces immediate exposure without requiring changes to the underlying table definition.

      1 Create the support role
      postgres=# CREATE ROLE dm_operation LOGIN;
      CREATE ROLE
      2 Review production environment considerations

      As a security best practice, sensitive database objects should be stored in dedicated schemas and protected through role-based permissions. Rather than relying on broad access granted through the PUBLIC role, permissions should be assigned explicitly to application and support roles. This approach provides more granular control over who can view or modify sensitive data and supports the principle of least privilege.

      The following statements remove default access and establish a controlled permission model for the demonstration environment:

      postgres=# REVOKE ALL ON SCHEMA data_masking_demo FROM PUBLIC;
      REVOKE
      postgres=# REVOKE ALL ON TABLE data_masking_demo.customer_sensitive FROM PUBLIC;
      REVOKE
      postgres=# REVOKE ALL ON TABLE data_masking_demo.customer_sensitive FROM dm_operation;
      REVOKE
      3 Create a temporary redacted view

      The next step is to provide the support role with a restricted view of the data while preventing direct access to sensitive information. In this example, a security-barrier view exposes only redacted values for the columns required by the operations team. This approach reduces immediate exposure without modifying the underlying table structure.

      postgres=# CREATE VIEW data_masking_demo.customer_sensitive_masked
      postgres-# WITH (security_barrier = true)
      postgres-# AS
      postgres-# SELECT
      postgres-#     customer_id,
      postgres-#     '[REDACTED]'::text AS full_name,
      postgres-#     regexp_replace(email, '(^.).*(@.*$)', '\1***\2') AS email,
      postgres-#     regexp_replace(
      postgres(#         phone,
      postgres(#         '^(.{3})-.{4}-.{4}$',
      postgres(#         '\1-****-****'
      postgres(#     ) AS phone,
      postgres-#     0::numeric(12,2) AS salary,
      postgres-#     date_trunc('day', created_at)::timestamp AS created_at
      postgres-# FROM data_masking_demo.customer_sensitive;
      CREATE VIEW

      Note: The id_document column contains binary document data stored as bytea. Since the operations role does not require access to document contents and bytea is outside the documented masking matrix, the column is excluded from the view. When there is no business need to expose sensitive data, omission is often the safest short-term containment strategy.

      4 Grant access only to the protected view

      The dm_operation role is granted access only to the redacted view, not the original table. This ensures that users see the protected representation of the data while access to the underlying sensitive values remains restricted.

      postgres=# GRANT USAGE
      postgres-# ON SCHEMA data_masking_demo
      postgres-# TO dm_operation;
      GRANT
      postgres=# GRANT SELECT
      postgres-# ON data_masking_demo.customer_sensitive_masked
      postgres-# TO dm_operation;
      GRANT
      5 Test the containment

      The results show that the containment measure is effective. The dm_operation role can access the redacted view but cannot query the underlying table, ensuring that only protected values are exposed. While this reduces immediate risk, it does not resolve the underlying masking limitation. The unsupported text and bytea columns still require a long-term remediation strategy.

      postgres=# SELECT session_user, current_user;
       session_user | current_user
      --------------+--------------
       dm_operation | dm_operation
      (1 row)
      
      postgres=# SELECT *
      postgres-# FROM data_masking_demo.customer_sensitive_masked
      postgres-# ORDER BY customer_id;
       customer_id | full_name  |      email      |     phone     | salary |     created_at
      -------------+------------+-----------------+---------------+--------+---------------------
                 1 | [REDACTED] | xxx@example.com | 012-****-**** |   0.00 | 2026-07-20 00:00:00
                 2 | [REDACTED] | xxx@example.com | 012-****-**** |   0.00 | 2026-07-20 00:00:00
      (2 rows)
      
      postgres=# SELECT *
      postgres-# FROM data_masking_demo.customer_sensitive;
      ERROR:  permission denied for table customer_sensitive

      Other containment choices

      The temporary redacted view is not the only valid response. Depending on application requirements and the organization's security model, other containment measures may include:

      • Restricting access through table-level and role-based permissions
      • Using column-level privileges to prevent access to unsupported sensitive columns
      • Moving sensitive fields into separately protected tables with tighter access controls
      • Providing controlled application interfaces that expose only the information required for operational tasks

      Confirm the root cause

      After access is contained, repeat the original pgx_create_confidential_policy call against the unchanged text column. The policy should still be rejected. This separates the temporary access workaround from the underlying data-type limitation and confirms that a longer-term design decision is still required.

      Long-term resolution

      Once the immediate exposure has been contained, the organization must decide whether to retain the existing text column and rely on privilege-based controls as the permanent design, or align the column with the documented masking matrix so that Fujitsu Enterprise Postgres Data Masking can be applied directly.

      For this example, the email column is migrated from text to varchar(320). This approach preserves the logical purpose of the column while making it eligible for documented regular expression masking.

      An alternative approach would be to retain the existing text column and continue protecting the data through a combination of role-based permissions and a redacted view. The most appropriate solution depends on the organization's security requirements, operational constraints, and application design.

      1. Validate the existing data before changing the data type
      postgres=# SELECT max(length(email)) AS maximum_email_length
      postgres-# FROM data_masking_demo.customer_sensitive;
       maximum_email_length
      ----------------------
                         25
      (1 row)
      
      postgres=# SELECT customer_id, length(email) AS email_length
      postgres-# FROM data_masking_demo.customer_sensitive
      postgres-# WHERE length(email) > 320;
       customer_id | email_length
      -------------+--------------
      (0 rows)

      Before converting the email column, review the existing data to ensure that all values fit within the proposed limit.

      2. Change the column to a supported data type

      The next step is to convert the email column from text to varchar(320) so that it can be protected using a documented masking policy. Because the temporary redacted view depends on the column, it must be removed before the schema change can proceed.

      postgres=# ALTER TABLE data_masking_demo.customer_sensitive
      postgres-#     ALTER COLUMN email TYPE varchar(320)
      postgres-#     USING email::varchar(320);
      ERROR:  cannot alter type of a column used by a view or rule
      DETAIL: rule _RETURN on view data_masking_demo.customer_sensitive_masked depends on column "email"

      After the conversion is complete, the masking policy can be implemented and validated. The temporary view can then be retired, as masking is now applied directly to the underlying table.

      postgres=# DROP VIEW data_masking_demo.customer_sensitive_masked;
      DROP VIEW
      postgres=# ALTER TABLE data_masking_demo.customer_sensitive
      postgres-#     ALTER COLUMN email TYPE varchar(320)
      postgres-#     USING email::varchar(320);
      ALTER TABLE
      
      3. Verify the definition

      After the data type conversion, verify that the table definition reflects the expected change. The following query checks the column names, data types and any defined maximum lengths for the table.

      postgres=# SELECT
      postgres-#     column_name,
      postgres-#     data_type,
      postgres-#     character_maximum_length
      postgres-# FROM information_schema.columns
      postgres-# WHERE table_schema = 'data_masking_demo'
      postgres-#   AND table_name = 'customer_sensitive'
      postgres-# ORDER BY ordinal_position;
       column_name |          data_type          | character_maximum_length
      -------------+-----------------------------+--------------------------
       customer_id | bigint                      |
       full_name   | text                        |
       email       | character varying           |                      320
       phone       | character varying           |                       13
       salary      | numeric                     |
       created_at  | timestamp without time zone |
       id_document | bytea                       |
      (7 rows)

      Build a maintainable role policy

      Rather than embedding individual usernames directly in the masking policy, a role-based design is easier to manage and scale. Users who require access to unmasked data can be placed in a dedicated role, while all other users receive masked values by default. This approach simplifies ongoing administration and avoids the need to modify the policy whenever personnel change.

      1. Create a role for users who require unmasked access

      Create a group role to represent users authorized to view unmasked data, and grant membership to the appropriate accounts.

      postgres=# CREATE ROLE dm_authorized NOLOGIN;
      CREATE ROLE
      postgres=# CREATE ROLE dm_admin LOGIN;
      CREATE ROLE
      postgres=# GRANT dm_authorized TO dm_admin;
      GRANT ROLE

      The masking policy can then check whether the connected user belongs to the authorized role and apply masking accordingly.

      postgres=> SELECT
      postgres->     session_user,
      postgres->     pg_has_role(session_user, 'dm_authorized', 'MEMBER') AS is_authorized,
      postgres->     NOT pg_has_role(session_user, 'dm_authorized', 'MEMBER') AS should_mask;
       session_user | is_authorized | should_mask
      --------------+---------------+-------------
       dm_admin     | t             | f
      (1 row)

      Policy behavior:
      Authorized member → should_mask = false → Sees unmasked data
      Not an authorized member → should_mask = true → Sees masked data

      Using session_user ensures that the masking decision is based on the account that established the connection, providing a consistent and maintainable role-based access model. In production environments, validate the behavior against any nested role structures, connection pooling configurations and SET ROLE usage patterns.

      2. Create the table-level policy and mask email

      With the role structure in place, the next step is to create a masking policy for the table. The policy evaluates the connected user's role membership and determines whether masking should be applied. In this example, the email column is protected using regular expression masking to hide the local part of the email address while preserving the domain.

      postgres=# SELECT pgx_create_confidential_policy(
      postgres(#     schema_name        := 'data_masking_demo',
      postgres(#     table_name         := 'customer_sensitive',
      postgres(#     policy_name        := 'customer_masking_policy',
      postgres(#     expression         :=
      postgres(#         'NOT pg_has_role(session_user, ''dm_authorized'', ''MEMBER'')',
      postgres(#     enable             := true,
      postgres(#     policy_description :=
      postgres(#         'Masks supported customer fields unless the login is a member of dm_authorized',
      postgres(#     column_name        := 'email',
      postgres(#     function_type      := 'REGEXP',
      postgres(#     regexp_pattern     := '(.*)@(.*)',
      postgres(#     regexp_replacement := 'xxx\2',
      postgres(#     regexp_flags       := 'g',
      postgres(#     column_description :=
      postgres(#         'Masks the email local part and preserves the domain'
      postgres(# );
       pgx_create_confidential_policy
      --------------------------------
       t
      (1 row)

      Users who are members of dm_authorized see the original email address, while all other users receive a masked value such as xxx@example.com.

      Note: Only one masking policy can be created for a table. Additional supported columns must be added to the existing policy rather than creating separate policies. The remaining supported columns are added in the following steps.

      3 Protecting multiple types of sensitive data with a single policy

      A confidential policy is not limited to a single sensitive column. Once the policy has been created on a table, additional columns can be added with masking functions that match the sensitivity and business requirements of each data type. In this example, the policy is extended to protect phone numbers, salary information, and timestamps using a combination of partial and full masking techniques.

      3.1 Add partial masking for phone numbers

      The phone number is added to the existing table policy using the PARTIAL masking function. This configuration preserves the first three digits while masking the remainder of the value, allowing users to identify a record without exposing the full phone number.

      postgres=# SELECT pgx_alter_confidential_policy(
      postgres(#     schema_name         := 'data_masking_demo',
      postgres(#     table_name          := 'customer_sensitive',
      postgres(#     policy_name         := 'customer_masking_policy',
      postgres(#     action              := 'ADD COLUMN',
      postgres(#     column_name         := 'phone',
      postgres(#     function_type       := 'PARTIAL',
      postgres(#     function_parameters :=
      postgres(#         'VVV-VVVV-VVVV, VVV-VVVV-VVVV, *, 4, 11',
      postgres(#     column_description  :=
      postgres(#         'Preserves the first three digits and masks the remainder'
      postgres(# );
       pgx_alter_confidential_policy
      --------------------------------
       t
      (1 row)
      3.2 Add full masking for salary

      Salary values are highly sensitive and generally have no business requirement to remain partially visible to unauthorized users. In this case, the FULL masking function completely obscures the data, ensuring that users who do not have the appropriate privileges cannot view any portion of the salary amount.

      postgres=# SELECT pgx_alter_confidential_policy(
      postgres(#     schema_name        := 'data_masking_demo',
      postgres(#     table_name         := 'customer_sensitive',
      postgres(#     policy_name        := 'customer_masking_policy',
      postgres(#     action             := 'ADD_COLUMN',
      postgres(#     column_name        := 'salary',
      postgres(#     function_type      := 'FULL',
      postgres(#     column_description := 'Fully masks salary'
      postgres(# );
       pgx_alter_confidential_policy
      --------------------------------
       t
      (1 row)
      3.3 Mask the precise time while retaining the date

      Not all timestamp data needs to be hidden entirely. In many reporting and auditing scenarios, the date remains useful while the exact time may be considered sensitive. This PARTIAL masking configuration preserves the year, month, and day while replacing the time component with 00:00:00. The result provides business context without revealing precise user activity times.

      postgres=# SELECT pgx_alter_confidential_policy(
      postgres(#     schema_name         := 'data_masking_demo',
      postgres(#     table_name          := 'customer_sensitive',
      postgres(#     policy_name         := 'customer_masking_policy',
      postgres(#     action              := 'ADD_COLUMN',
      postgres(#     column_name         := 'created_at',
      postgres(#     function_type       := 'PARTIAL',
      postgres(#     function_parameters := 'MDYh0m0s0',
      postgres(#     column_description  :=
      postgres(#         'Preserves the date and returns 00:00:00 as the time'
      postgres(# );
       pgx_alter_confidential_policy
      --------------------------------
       t
      (1 row)
      4. Protect columns outside the masking matrix

      Although the masking policy now protects the supported columns, it does not automatically secure unsupported data types such as full_name (text) or id_document (bytea).

      The following permissions limit the dm_operation role to only the columns covered by the masking strategy, while dm_admin retains access to the entire table. This helps prevent accidental exposure of data that is outside the masking matrix.

      postgres=# REVOKE ALL
      postgres-# ON data_masking_demo.customer_sensitive
      postgres-# FROM dm_operation,
      postgres-#      dm_admin;
      REVOKE
      postgres=# GRANT USAGE
      postgres-# ON SCHEMA data_masking_demo
      postgres-# TO dm_operation, dm_admin;
      GRANT
      postgres=# GRANT SELECT
      postgres-#     (customer_id, email, phone, salary, created_at)
      postgres-# ON data_masking_demo.customer_sensitive
      postgres-# TO dm_operation;
      GRANT
      postgres=# GRANT SELECT
      postgres-# ON data_masking_demo.customer_sensitive
      postgres-# TO dm_admin;
      GRANT
      5. Validate what each user sees

      A masking policy should always be tested from the perspective of the users who will access the data. This confirms that authorized users see the original values while other users see only the masked representations.

      5.1 Check the policy definition

      Before testing user access, confirm that the policy and its associated column definitions have been created correctly.

      postgres=# SELECT
      postgres-#     schema_name,
      postgres-#     table_name,
      postgres-#     policy_name,
      postgres-#     expression,
      postgres-#     enable,
      postgres-#     policy_description
      postgres-# FROM pgx_confidential_policies
      postgres-# WHERE schema_name = 'data_masking_demo'
      postgres-#   AND table_name = 'customer_sensitive';
       schema_name       | table_name         | policy_name             | expression                                                  | enable | policy_description
      -------------------+--------------------+-------------------------+-------------------------------------------------------------+--------+-------------------------------------------------------------------------------
       data_masking_demo | customer_sensitive | customer_masking_policy | NOT pg_has_role(session_user, 'dm_authorized', 'MEMBER')    | t      | Masks supported customer fields unless the login is a member of dm_authorized
      (1 row)
      postgres=# SELECT
      postgres-#     column_name,
      postgres-#     function_type,
      postgres-#     function_parameters,
      postgres-#     regexp_pattern,
      postgres-#     regexp_replacement,
      postgres-#     regexp_flags
      postgres-# FROM pgx_confidential_columns
      postgres-# WHERE schema_name = 'data_masking_demo'
      postgres-#   AND table_name = 'customer_sensitive'
      postgres-# ORDER BY column_name;
       column_name | function_type |       function_parameters              | regexp_pattern | regexp_replacement | regexp_flags
      -------------+---------------+----------------------------------------+----------------+--------------------+--------------
       created_at  | PARTIAL       | MDYh0m0s0                              |                |                    |
       email       | REGEXP        |                                        | (.*)@(.*)      | xxx\2              | g
       phone       | PARTIAL       | VVV-VVVV-VVVV, VVV-VVVV-VVVV, *, 4, 11 |                |                    |
       salary      | FULL          |                                        |                |                    |
      (4 rows)

      These queries allow administrators to verify that the policy is enabled and that each protected column has been configured with the intended masking function.

      5.2 Test the operation user experience

      Connect using the dm_operation account and query the protected table. This verifies that sensitive information is protected while still allowing the support team to perform their operational responsibilities.

      postgres=> SELECT
      postgres->     customer_id,
      postgres->     email,
      postgres->     phone,
      postgres->     salary,
      postgres->     created_at
      postgres-> FROM data_masking_demo.customer_sensitive
      postgres-> ORDER BY customer_id;
       customer_id |        email        |      phone      | salary |     created_at
      -------------+---------------------+-----------------+--------+---------------------
                 1 | xxx@example.com     | 012-****-****   |      0 | 2026-07-20 00:00:00
                 2 | xxx@example.com     | 012-****-****   |      0 | 2026-07-20 00:00:00
      (2 rows)
      5.3 Test the authorized administrator experience

      Connect in a separate session as dm_admin and query the protected table. As dm_admin is a member of the dm_authorized role, the masking expression evaluates to false and the policy does not apply masking. The administrator therefore sees the original values stored in the table, including the full email address, phone number, salary and timestamp.

      This confirms that the role-based policy is working as intended: authorized users receive unmasked data, while users who are not members of the authorized role receive masked values.

      postgres=> SELECT
      postgres->     customer_id,
      postgres->     email,
      postgres->     phone,
      postgres->     salary,
      postgres->     created_at
      postgres-> FROM data_masking_demo.customer_sensitive
      postgres-> ORDER BY customer_id;
       customer_id |          email            |     phone     | salary  |     created_at
      -------------+---------------------------+---------------+---------+---------------------
                 1 | diksha.sharma@example.com | 012-3456-7890 | 5000.00 | 2026-07-20 10:15:30
                 2 | john.smith@example.com    | 012-3456-7891 | 8000.00 | 2026-07-20 11:20:45
      (2 rows)

      Summary

      Unsupported data types do not mean sensitive data cannot be protected. They simply require a different control strategy. In this blog post, we showed how a temporary containment approach reduced immediate exposure while the root cause was investigated and remediated. The chosen long-term solution aligned the email column with the documented masking matrix, enabling Fujitsu Enterprise Data Masking to be applied directly. At the same time, a role-based access model and least-privilege permissions continued to protect data that remained outside the masking matrix, such as text and bytea columns.

      The key lesson is that effective data protection is not achieved by masking alone. It requires a combination of appropriate data types, well-defined access controls, and a clear understanding of the capabilities and limitations of the platform. By validating sensitive columns against the documented masking matrix during design and applying the right protection mechanism for each data type, organizations can build practical, maintainable, and production-ready security controls that evolve with their applications and operational requirements.

      Topics: PostgreSQL, Database security, Data Masking, Fujitsu Enterprise Postgres, Data governance, Security

      Receive our blog

      Search by topic

      see all >
      Diksha Sharma
      Associate Application Developer
      Diksha Sharma is an Associate Application Developer at Fujitsu Australia, working on PostgreSQL‑based solutions within the Fujitsu Enterprise Postgres ecosystem.
      Her work focuses on customer support, performance analysis, and troubleshooting, alongside developing tools to streamline log analysis and reduce issue resolution time.She also contributes to AI‑driven solutions, including ML, LLM, and GenAI, using PostgreSQL extensions for intelligent data processing and retrieval.
      Fujitsu Enterprise Postgres
      is an enhanced distribution of PostgreSQL, 100% compatible and with extended features.
      Compare the list of features.
      roundel-owl-and-book-01PostgreSQL Insider
      has a series of technical articles for PostgreSQL enthusiasts of all stripes, with tips and how-to's.
      Explore PostgreSQL Insider >
      Subscribe to be notified of future blog posts
      If you would like to be notified of my next blog posts and other PostgreSQL-related articles, fill the form here.

      Read our latest blogs

      Read our most recent articles regarding all aspects of PostgreSQL and Fujitsu Enterprise Postgres.

      Receive our blog

      Fill the form to receive notifications of future posts

      Search by topic

      see all >