Compatibility comparisons between AnalyticDB for PostgreSQL V6.0 and V7.0
AnalyticDB for PostgreSQL V6.0 and V7.0 are not fully compatible. Before upgrading an instance from V6.0 to V7.0, review the changes in this document to identify compatibility issues in your existing SQL, application code, and business logic.
This document covers the following changes:
-
Data types — removed types and UNKNOWN type behavior
-
SQL syntax — primary key propagation, CREATE FUNCTION, and OID columns
-
System tables — deleted, added, and modified system tables
-
Extensions — extensions unavailable in V7.0 and their replacements
-
Keywords — keyword additions, removals, and category changes
-
Functions — renamed directories and changed function behaviors
-
Others — table name length limit and password encryption upgrade
The following changes can cause existing SQL statements to fail and require code changes before you upgrade. Review these areas with highest priority:
-
UNKNOWN type handling in CTAS statements and function calls (see Data types)
-
System tables deleted or modified (see System tables)
-
Extensions replaced or unavailable (see Extensions)
-
Keywords removed or recategorized that may conflict with object names (see Keywords)
Data types
Removed types
V7.0 removes the following legacy types. Replace them with standard SQL equivalents such as TIMESTAMP:
-
ABSTIME -
REALTIME -
TINTERVAL -
TIMEINTERVAL
UNKNOWN type
This change can cause existing SQL statements to fail. Review all queries that rely on implicit UNKNOWN type resolution before upgrading.
V7.0 reserves the UNKNOWN keyword but no longer supports it as a data type. The following behavior changes apply:
Column declarations: Specifying UNKNOWN as a column type returns an error.
CREATE TABLE test(a INT, b UNKNOWN);
ERROR: COLUMN "b" has pseudo-type UNKNOWN
CREATE TABLE AS SELECT (CTAS) statements: V7.0 no longer treats string constants as UNKNOWN. Instead, it applies the following conversion rules:
-
An ordinary string constant or
NULLis converted toTEXT.CREATE TABLE test AS SELECT 1 AS a, NULL AS b; -- TABLE "public.test" -- Column | Type | Collation | Nullable | Default -- -------+---------+-----------+----------+--------- -- a | integer | | | -- b | text | | | -- Distributed randomly -
With a
UNIONclause, the constant is converted to the data type of the nearest column (nearest neighbor rule).CREATE TABLE test AS SELECT 1::INT UNION SELECT NULL; -- TABLE "public.test" -- Column | Type | Collation | Nullable | Default -- -------+---------+-----------+----------+--------- -- int4 | integer | | | -- Distributed randomly -
If the nearest neighbor rule cannot resolve the type (for example, due to nested
SELECTstatements), the constant falls back toTEXT, which may cause a type mismatch error.CREATE TABLE test(a INT); -- The string constant in the inner SELECT cannot resolve to INT INSERT INTO test SELECT a FROM (SELECT '1' AS a) t; ERROR: COLUMN "a" IS OF type INTEGER but expression IS OF type text
Function calls: Passing a string constant with an incompatible argument type may return an UNKNOWN type error.
-- string_agg() only accepts these signatures:
-- string_agg(text, text)
-- string_agg(bytea, bytea)
CREATE TABLE test(a INT, b INT);
SELECT a, string_agg(b, ',') FROM test GROUP BY a;
ERROR: FUNCTION string_agg(INTEGER, UNKNOWN) does NOT exist
V6.3.11 already applies the same UNKNOWN type restrictions as V7.0. In V6.6.2, the adbpg_enable_resolve_unknowns parameter controls this behavior — UNKNOWN type support is disabled by default.
SQL syntax
ALTER TABLE ... ADD PRIMARY KEY
In V7.0, when you add a primary key with the NOT NULL property to a parent table, the NOT NULL constraint is also propagated to the corresponding columns of all child tables. In V6.0, this propagation does not occur.
INSERT ON CONFLICT ... DO UPDATE SET
In V7.0, the INSERT ON CONFLICT ... DO UPDATE SET statement enforces stricter syntax for multi-column assignment. The right side of the equals sign must be a subquery or a ROW() expression. Multi-value list assignment is no longer supported.
In V6.0, the (a, b, c) = (newa, newb, newc) multi-value list syntax is allowed.
Example:
--- V6.0 multi-value list syntax (supported)
INSERT INTO t1(c1, c2, c3) VALUES (1, 2, 3)
ON CONFLICT(c1) DO UPDATE SET (c2, c3) = (EXCLUDED.c2, EXCLUDED.c3);
--- V7.0 ROW() expression syntax (required)
INSERT INTO t1(c1, c2, c3) VALUES (1, 2, 3)
ON CONFLICT(c1) DO UPDATE SET (c2, c3) = ROW(EXCLUDED.c2, EXCLUDED.c3);
CREATE FUNCTION
In V7.0, the CREATE FUNCTION statement no longer supports a WITH clause. Review any function definitions that use WITH before upgrading.
Window function RANGE offset type
In V7.0, when you specify an offset by using RANGE in a window function, the offset parameter type must strictly match the ORDER BY column type. If the ORDER BY column is a date type, you must explicitly specify the offset as the interval type. The integer type is no longer compatible.
In V6.0, the type matching requirement is less strict, and the integer type is allowed as the offset for date columns.
Example:
--- V6.0 integer offset for date columns (supported)
SELECT sum(c1) OVER (
ORDER BY c2 DESC
RANGE BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING
);
--- V7.0 explicit interval type (required)
SELECT sum(c1) OVER (
ORDER BY c2 DESC
RANGE BETWEEN INTERVAL '1 day' FOLLOWING AND UNBOUNDED FOLLOWING
);
OID columns
V6.0 stores object identifiers (OIDs) as hidden columns in system tables. You can add a hidden OID column to a new table using WITH OIDS in the CREATE TABLE statement.
V7.0 changes this behavior in two ways:
-
System tables expose OIDs as ordinary columns, not hidden columns.
-
The
WITH OIDSclause is disabled. To create a table with OID columns, declare them explicitly in the column list.
System tables
V7.0 modifies several system tables. If your application logic queries these tables directly, update the queries before upgrading.
Deleted system tables
| System table | Replacement |
|---|---|
pg_exttable |
Merged into pg_foreign_table. The server column is renamed to gp_exttable_server, and external table properties are stored in the ftoptions column. |
pg_partition |
Replaced by gp_partition_template and pg_partitioned_table. |
pg_partition_encoding |
Replaced by gp_partition_template and pg_partitioned_table. |
pg_partition_rule |
Replaced by gp_partition_template and pg_partitioned_table. |
Added system tables
gp_partition_template
Defines the template for each partition level in a partitioned table.
| Column name | Type | Description |
|---|---|---|
relid |
oid | The OID of the parent partitioned table. |
level |
int16 | The level of the partition in the partitioned table. |
template |
pg_node_tree | The template structure. |
pg_partitioned_table
Stores partitioning information for each partitioned table.
| Column name | Type | Reference | Description |
|---|---|---|---|
partrelid |
oid | pg_class.oid |
The OID of the pg_class entry for the partitioned table. |
partstrat |
char | — | The partitioning strategy: h (hash), l (list), r (range). |
partnatts |
int2 | — | The number of columns in the partition key. |
partdefid |
oid | pg_class.oid |
The OID of the default partition. 0 if no default partition exists. |
partattrs |
int2vector | pg_attribute.attnum |
An array indicating which columns form the partition key. A value of 0 indicates an expression rather than a column reference. |
partclass |
oidvector | pg_opclass.oid |
The OID of the operator class for each partition key column. |
partcollation |
oidvector | pg_opclass.oid |
The OID of the collation for each partition key column. 0 if the column type is not collatable. |
partexprs |
pg_node_tree | — | Expression trees for partition key columns that are not simple column references. NULL if all columns are simple references. |
Modified system tables
pg_attribute
| Change | Description |
|---|---|
Added atthasmissing |
Whether the column has missing values. true: can contain NULL; false: cannot. |
Added attidentity |
Whether the column is an identity column. 'a': always generated; 'd': generated by default; '': not an identity column. |
Added attgenerated |
Whether the column is a generated column. 's': value is stored; '': not a generated column. |
pg_class
| Change | Description |
|---|---|
Deleted relstorage |
The physical storage mode indicator is removed. For external tables, relkind now uses 'f' instead of 'r'. |
Deleted relhasoids |
Removed because hidden OID columns are no longer supported. |
Deleted relhaskey |
Removed. To check whether a table has a primary key index, query pg_index instead. |
Added relrowsecurity |
Whether row-level security (RLS) is enabled for the table. true: RLS enabled; false: disabled (all users can access all rows by default). |
Added felforcerowsecurity |
Whether RLS is enforced for the table owner. If true, RLS policies apply even to the table owner. |
Added relispartition |
Whether the table is a partition. true: the table is a partition; false: the table is not partitioned or is a parent table. |
Added relrewrite |
Whether the table defines DDL rewrite rules. true: rewrite rules defined; false: no rewrite rules. |
Added relpartbound |
The internal representation of the partition bound (type: pg_node_tree). |
pg_index
| Change | Description |
|---|---|
Added indnkeyatts |
The number of key columns in the index. V7.0 supports the INCLUDE clause in CREATE INDEX, which allows non-key columns to be added to leaf nodes for faster lookups. |
pg_proc
| Change | Description |
|---|---|
Renamed protransform to prosupport |
The support function reference. The function pointed to by prosupport is simpler than its predecessor. |
Deleted proisagg |
Replaced by prokind. |
Deleted proiswindow |
Replaced by prokind. |
Added prokind |
The function type: f (common function), p (stored procedure), a (aggregate function), w (window function). |
Added proparallel |
Whether the function is safe for parallel execution: s (safe), r (restricted to parallel leader), u (unsafe — forces serial execution). |
Added protrftypes |
The OIDs of data type transform rules to apply. |
pg_statistic
| Change | Description |
|---|---|
Added stacoll |
The collation used when collecting statistics. A non-zero value (N) indicates the collation used for a particular slot; 0 means statistics cannot be collected. |
Extensions
The following extensions are unavailable in V7.0. Extensions marked as Replaced have a supported alternative — update your code to use the replacement before upgrading. Extensions marked as Temporarily unavailable have no current alternative in V7.0.
| Extension | Description | Status in V7.0 |
|---|---|---|
oss_ext |
Object Storage Service (OSS) foreign table protocol. | Replaced by `oss_fdw` |
PL/Python |
Python code embedded in a PostgreSQL database. | Replaced by `PL/Python3u` |
PL/Python2u |
Python 2 stored procedures and functions. | Replaced by `PL/Python3u` |
adbpg_desensitization |
SQL statement masking. | Temporarily unavailable |
adbpg_hardware_bench |
Hardware suitability evaluation. | Temporarily unavailable |
address_standardizer |
Address resolution into component elements. | Temporarily unavailable |
address_standardizer_data_us |
US address standardization. | Temporarily unavailable |
auto_partition |
Date-based partition management. | Temporarily unavailable |
diskquota |
Disk quota management. | Temporarily unavailable |
fastann |
Vector database engine. | Temporarily unavailable |
hyjal_pb_formatter |
Hyjal protobuf data reader. | Temporarily unavailable |
madlib |
Open source in-database analytics library. | Temporarily unavailable |
morton_code |
Morton encoder. | Temporarily unavailable |
multi_master |
User-defined function (UDF) for the multi-master feature. | Temporarily unavailable |
Multicorn |
Custom foreign data source queries (requires Python). | Temporarily unavailable |
open_analytic |
Unstructured data analysis. | Temporarily unavailable |
PL/Java |
Java code embedded in a PostgreSQL database. | Temporarily unavailable |
redis_fdw |
Foreign data wrapper (FDW) for Redis. | Temporarily unavailable |
Keywords
V7.0 adds, modifies, and removes specific keywords. Database object names cannot conflict with reserved keywords.
To query the keywords and their categories in your instance, run:
SELECT * FROM pg_get_keywords();
Keyword categories:
| Category code | Description |
|---|---|
U |
Unreserved. Can be used as any object name, including views, tables, functions, indexes, columns, and types. |
C |
Unreserved. Can be used as any object name except function and type names. |
T |
Reserved. Can be used only as function or type names. |
R |
Reserved. Cannot be used as any object name. |
The following table lists all keyword changes between V6.0 and V7.0.
| Keyword | V6.0 | V7.0 |
|---|---|---|
access_key_id |
Unreserved | N/A (removed) |
attach |
N/A | Unreserved |
call |
N/A | Unreserved |
columns |
N/A | Unreserved |
coordinator |
N/A | Unreserved |
cube |
Unreserved (cannot be function or type name) | Unreserved |
depends |
N/A | Unreserved |
detach |
N/A | Unreserved |
endpoint |
N/A | Unreserved |
generated |
N/A | Unreserved |
groups |
N/A | Unreserved |
import |
N/A | Unreserved |
include |
N/A | Unreserved |
incremental |
Unreserved | N/A (removed) |
initplan |
N/A | Unreserved |
json |
Unreserved | N/A (removed) |
jsonline |
Unreserved | N/A (removed) |
lc_collate |
Unreserved | N/A (removed) |
lc_ctype |
Unreserved | N/A (removed) |
library |
Unreserved | N/A (removed) |
locked |
N/A | Unreserved |
logged |
N/A | Unreserved |
manifest |
Unreserved | N/A (removed) |
merge |
Unreserved | N/A (removed) |
method |
N/A | Unreserved |
multisort |
Unreserved | N/A (removed) |
new |
N/A | Unreserved |
old |
N/A | Unreserved |
orc |
Unreserved | N/A (removed) |
overriding |
N/A | Unreserved |
parquet |
Unreserved | N/A (removed) |
persistently |
N/A | Unreserved |
policy |
N/A | Unreserved |
procedures |
N/A | Unreserved |
referencing |
N/A | Unreserved |
retrieve |
N/A | Unreserved |
rollup |
Unreserved (cannot be function or type name) | Unreserved |
routine |
N/A | Unreserved |
routines |
N/A | Unreserved |
schemas |
N/A | Unreserved |
secret_access_key |
Unreserved | N/A (removed) |
sets |
Unreserved (cannot be function or type name) | Unreserved |
skip_ao_aux_table |
Unreserved | N/A (removed) |
skip |
N/A | Unreserved |
sort |
Unreserved | N/A (removed) |
sorted |
Unreserved | N/A (removed) |
storage_cold |
Unreserved | N/A (removed) |
storage_hot |
Unreserved | N/A (removed) |
stored |
N/A | Unreserved |
support |
N/A | Unreserved |
synchronization |
Unreserved | N/A (removed) |
tablesample |
N/A | Reserved (cannot be function or type name) |
transform |
N/A | Unreserved |
ttl |
Unreserved | N/A (removed) |
unload |
Unreserved | N/A (removed) |
unsorted |
Unreserved | N/A (removed) |
xmlnamespaces |
N/A | Unreserved (cannot be function or type name) |
xmltable |
N/A | Unreserved (cannot be function or type name) |
zorder |
Reserved | N/A (removed) |
Functions
Renamed write-ahead log directories and functions
V7.0 renames the write-ahead log (WAL) directory from pg_xlog to pg_wal and the transaction status directory from pg_clog to pg_xact. All xlog references in SQL functions, tools, and options are renamed to wal. Update any scripts or automation that reference these names.
| Old name | New name |
|---|---|
pg_xlog (directory) |
pg_wal |
pg_clog (directory) |
pg_xact |
pg_switch_xlog() |
pg_switch_wal() |
pg_receivexlog |
pg_receivewal |
--xlogdir |
--waldir |
Changed substring() behavior
The SQL-style substring() function now uses standard-compliant greedy behavior. When a pattern matches in multiple ways, the initial sub-pattern matches the smallest possible amount of text rather than the largest. For example, the pattern %#"aa*#"% now selects the first group of a characters from the input instead of the last group.
CASE WHEN statements with Set-Returning Functions (SRFs)
V7.0 enforces stricter type consistency checks in CASE WHEN statements, requiring branch return values to be scalar types. SRFs (functions with a SETOF return type, such as generate_series, regexp_matches, and unnest) must be rewritten to return scalar output or replaced with equivalent logic.
In V6.0, some scenarios allow CASE WHEN branches to return multiple rows directly.
The following examples show common SRF rewrites:
-
unnest: Moveunnestoutside theCASE WHENblock so that each branch returns an array type (scalar).--- V6.0 unnest inside CASE WHEN (supported) SELECT CASE c1 WHEN 1 THEN unnest(array[1::bigint, 2::bigint]) WHEN 3 THEN 4::bigint ELSE c1::bigint END FROM t1; --- V7.0 unnest moved outside (required) SELECT unnest( CASE c1 WHEN 1 THEN array[1, 2]::bigint[] WHEN 3 THEN array[4]::bigint[] ELSE array[c1]::bigint[] END ) FROM t1; -
regexp_matches: For cases whereregexp_matches(text, pattern)matches only once, replace it withsubstring(text from pattern), which returns a scalar string.--- V6.0 regexp_matches inside CASE WHEN (supported) SELECT CASE WHEN c1 IS NULL THEN NULL ELSE regexp_matches(c1, '[0-9]+[.][0-9]+') END FROM t1; --- V7.0 replaced with substring (required) SELECT CASE WHEN c1 IS NULL THEN NULL ELSE substring(c1 FROM '[0-9]+[.][0-9]+') END FROM t1; -
generate_series: UseUNION ALLto split the logic and avoid using SRFs insideCASE WHEN.--- V6.0 generate_series inside CASE WHEN (supported) SELECT CASE WHEN c1 = 1 THEN 1 ELSE generate_series(1, 10) END AS c2 FROM t1; --- V7.0 rewritten with UNION ALL (required) SELECT 1 AS c2 FROM t1 WHERE c1 = 1 UNION ALL SELECT generate_series(1, 10) FROM t1 WHERE c1 <> 1 OR c1 IS NULL;
Others
-
Table name length limit:
-
V7.0 supports table names up to 128 characters, providing a more spacious namespace.
-
V6.0 supports table names up to 64 characters only. Names that exceed this limit are automatically truncated.
In V6.0, when a table name exceeds 64 characters, the stored name is the truncated version, and queries using the original name are resolved to the truncated name, keeping both sides consistent. If you use the data migration feature to migrate such a table to V7.0, the longer name limit means the original full name cannot access the table stored under the truncated name.
You can use
ALTER TABLE ... RENAME TOto restore the truncated name to the original full name.Example:
--- Table migrated from V6.0 has a truncated name in V7.0 CREATE TABLE very_long_table_name_that_exceeds_sixty_four_characters_limi ( c1 INT ); --- Rename the truncated table name to the original full name ALTER TABLE very_long_table_name_that_exceeds_sixty_four_characters_limi RENAME TO very_long_table_name_that_exceeds_sixty_four_characters_limit; -
-
Password encryption:
V7.0 uses SHA256 to encrypt database passwords instead of the MD5 algorithm used in V6.0. SHA256 provides stronger security.
After migrating data to a V7.0 instance, reset all database passwords to apply SHA256 encryption.
What's next
Use the data migration feature to migrate data from a V6.0 instance to a V7.0 instance. For details, see Migrate data between AnalyticDB for PostgreSQL instances.