Skip to main content

PostgreSQL Compatibility

SereneDB implements the SQL dialect of PostgreSQL. While we are able to syntactically parse any PostgreSQL-compliant statement, not all underlying functionality is implemented yet.

This page gives a non-exhaustive overview of currently supported core SQL functionality, followed by the behavioral differences where SereneDB intentionally diverges from PostgreSQL. SereneDB strives for full PostgreSQL compatibility, and most features not currently supported will be added over time.

For PostgreSQL-specific functionality such as system table support, see the System Table Compatibility page.

Data Definition

Table Creation & Deletion

FeatureSupport StateDetails
CREATE TABLEYes
DROP TABLEYes
Default ValuesYes
GENERATEDYes
Check ConstraintsYes
Not-Null ConstraintsYes
Unique ConstraintsYes
Primary KeysYes
Foreign KeysYesEnforced; over-eager on some UPDATEs
Named ConstraintsYes
Exclusion ConstraintsNoSee issue
System ColumnsNoSee issue

Table Modification (ALTER TABLE)

FeatureSupport StateDetails
ADD COLUMNYes
DROP COLUMNYes
ADD CHECKNoSee issue
ADD CONSTRAINTNoSee issue
ADD FOREIGN KEYNoSee issue
DROP CONSTRAINTNoSee issue
ALTER COLUMNPartialTYPE is supported; SET/DROP DEFAULT and SET NOT NULL are not. See issue
SET DEFAULTNoSee issue
DROP DEFAULTNoSee issue
COLUMN TYPEYes
RENAME COLUMNYes
RENAME TOYes

Privileges

FeatureSupport StateDetails
CREATE ROLENoSee issue
OWNER TONoSee issue
ALTER ROLENoSee issue
GRANTNoSee issue
REVOKENoSee issue
SET ROLEPartialAccepted as a no-op; not enforced
INHERITNoSee issue
Row Security PoliciesNoSee issue

Indexes

FeatureSupport StateDetails
CREATE INDEXYes
GINNo
BRINNo
Multicolumn IndexesYes
Ordered IndexesYes
Unique IndexesYes
Indexes on ExpressionsNo
Partial IndexesNo

Misc

FeatureSupport StateDetails
CREATE SCHEMAYes
DROP SCHEMAYes
search_pathYes
Table InheritanceNo
Table PartitioningNo
Foreign Data WrappersNo
ViewsYes
DatabasesYes
Functions & ProceduresYes
Custom TypesYes
TriggersNo
Prepared StatementsYes
Advisory LocksNo

Data Manipulation

FeatureSupport StateDetails
INSERTYes
UPDATEYes
DELETEYes
TRUNCATEYes
RETURNINGYes
COPY FROMYes
COPY TOYes
ON CONFLICTPartial

Queries

FeatureSupport StateDetails
Table & View ReferencesYes
Inner JoinsYes
Outer JoinsYes
SemijoinsYes
AntijoinsYes
Table FunctionsYes
Lateral SubqueriesYes
User-Specified AliasesYes
GROUP BYYes
HAVINGYes
GROUPING SETSYes
CUBEYes
ROLLUPYes
WINDOW FunctionsYes
WITHYes
WITH RECURSIVEYes
UNIONYes
UNION ALLYes
INTERSECTYes
INTERSECT ALLYes
EXCEPTYes
EXCEPT ALLYes
ORDER BYYes
LIMITYes
OFFSETYes
Table Generating FunctionYes

Data Types

Types

TypeSupport StateDetails
arrayYes
bigintYes
bigserialYes
bit [ (n) ]Yes
bit varying [ (n) ]Yes
booleanYes
boxNo
byteaYes
character [ (n) ]YesCHAR(n) not supported; use TEXT
character varying [ (n) ]YesVARCHAR works, VARCHAR(n) does not
cidrNo
circleNo
dateYes
double precisionYes
inetNo
integerYes
interval [ fields ] [ (p) ]Yes
jsonYes
jsonbNo
lineNo
lsegNo
macaddrNo
macaddr8No
moneyNo
numeric [ (p, s) ]Yes
pathNo
pg_lsnNo
pg_snapshotNo
pointNo
polygonNo
realYes
smallintYes
smallserialYes
serialYes
textYes
time [ (p) ] [ without time zone ]Partial
time [ (p) ] with time zoneYes
timestamp [ (p) ] [ without time zone ]Yes
timestamp [ (p) ] with time zoneYes
tsqueryYesSereneDB TSQUERY (inverted index), not PG tsvector/tsquery
tsvectorNo
txid_snapshotNo
uuidYes
xmlNo

Operators & Functions

Logical

FeatureSupport StateDetails
ANDYes
ORYes
NOTYes

Comparison

FeatureSupport StateDetails
<Yes
>Yes
<=Yes
>=Yes
=Yes
<>Yes
!=Yes
BETWEENYes
NOT BETWEENYes
IS DISTINCTYes
IS NOT DISTINCTYes
IS NULLYes
IS NOT NULLYes
IS TRUEYes
IS NOT TRUEYes
IS FALSEYes
IS NOT FALSEYes
IS UNKNOWNYes
IS NOT UNKNOWNYes

Mathematical

FeatureSupport StateDetails
+Yes
-Yes
*Yes
/Yes
%Yes
^Yes
/Yes
/
@No
&Yes
Yes
#No
~Yes
<<Yes
>>Yes
absYes
cbrtYes
ceilYes
degreesYes
divYes
erfYes
erfcYes
expYes
factorialYes
floorYes
gcdYes
lcmYes
lnYes
logYes
log10Yes
min_scaleNo
modYes
piYes
powerYes
radiansYes
roundYes
scaleNo
signYes
sqrtYes
trim_scaleNo
truncYes
width_bucketYes
randomYes
random_normalNo
setseedYes
acosYes
acosdYes
asinYes
asindYes
atanYes
atandYes
atan2Yes
atan2dYes
cosYes
cosdYes
cotYes
cotdYes
sinYes
sindYes
tanYes
tandYes
sinhYes
coshYes
tanhYes
asinhYes
acoshYes
atanhYes

Text

FeatureSupport StateDetails
||Yes
btrimNo
bit_lengthYes
char_lengthYes
lowerYes
lpadYes
ltrimYes
normalizeYes
octet_lengthYes
overlayYes
positionYes
rpadYes
rtrimYes
substringYesCurrently not supporting regular expression arguments
trimYes
upperYes
^@Yes
asciiYes
chrYes
concatYes
concat_wsYes
formatYes
initcapYes
leftYes
lengthYes
md5Yes
parse_identNo
quote_identYes
quote_literalYes
quote_nullableYes
repeatYes
replaceYes
reverseYes
rightYes
split_partYes
starts_withYes
string_to_arrayYes
string_to_tableNo
strposYes
substrYes
to_asciiNo
to_hexYes
translateYes
unistrNo

Bytea

FeatureSupport StateDetails
||Yes
bit_lengthYes
btrimNo
ltrimYes
octet_lengthYes
overlayYes
positionNo
rtrimYes
substringYes
trimYes
bit_countNo
get_bitYes
get_byteYes
lengthYes
md5Yes
set_bitYes
set_byteYes
sha224No
sha384No
sha512No
substrYes
convertNo
convert_fromYes
convert_toYes
encodeYes
decodeYes

Bit

FeatureSupport StateDetails
||Yes
&Yes
|Yes
#No
~Yes
<<Yes
>>Yes
bit_countYes
bit_lengthYes
lengthYes
octet_lengthYes
overlayNo
positionNo
substringNo
get_bitYes
set_bitYes

Pattern Matching

FeatureSupport StateDetails
LIKEYes
SIMILAR TOYes
regexp_countYes
regexp_instrYes
regexp_likeYes
regexp_matchYes
regexp_matchesPartial
regexp_replacePartialCurrently not supporting replacing N’th match
regexp_split_to_arrayYes
regexp_split_to_tableYes
regexp_substrYes

Data Type Formatting

FeatureSupport StateDetails
to_charNo
to_dateNo
to_numberNo
to_timestampPartialEpoch (numeric) form only; text+format parsing unsupported

Date/Time

FeatureSupport StateDetails
+Yes
-Yes
*Yes
/Yes
ageYes
clock_timestampYes
current_dateYes
current_timeYes
current_timestampYes
date_addYespossible with +
date_binYes
date_partYes
date_subtractNopossible with -
date_truncYesWithout timezone
extractYes
isfiniteYes
justify_daysNo
justify_hoursNo
justify_intervalNo
localtimeYes
localtimestampYes
make_dateYes
make_intervalNo
make_timeYes
make_timestampYes
make_timestamptzYesWith time zone name
nowYes
statement_timestampNo
timeofdayYes
transaction_timestampYes
to_timestampPartialEpoch (numeric) form only; text+format parsing unsupported
OVERLAPSNo
EXTRACTYes

Enum

FeatureSupport StateDetails
enum_firstYes
enum_lastYes
enum_rangeYes

Geometric

PostgreSQL's geometric types (point, line, lseg, box, path, polygon, circle) and their operators/functions are not supported. SereneDB instead provides spatial data through a built-in GEOMETRY type and ST_* functions (ST_Point, ST_Intersects, ST_Contains, ST_Distance, …). See Spatial Functions and the GEOMETRY type.

FeatureSupport StateDetails
+No
-No
*No
/No
@-@No
@@No
#No
##No
<->No
@>No
<@No
&&No
<<No
>>No
&<No
&>No
<<| NoNo
|>>No
&<| NoNo
&>No
<^No
>^No
?#No
?-No
?No
?-NoNo
?
~=No
areaNo
centerNo
diagonalNo
diameterNo
heightNo
isclosedNo
isopenNo
lengthNo
npointsNo
pcloseNo
popenNo
radiusNo
slopeNo
widthNo
boxNo
bounding_boxNo
circleNo
lineNo
lsegNo
pathNo
pointNo
polygonNo

Network

FeatureSupport StateDetails
<<No
<<=No
>>No
>>=No
&&No
~No
&No
|No
+No
-No
abbrevNo
broadcastNo
familyNo
hostNo
hostmaskNo
inet_mergeNo
inet_same_familyNo
masklenNo
netmaskNo
networkNo
set_masklenNo
textNo
FeatureSupport StateDetails
@@YesSereneDB full-text search (inverted index), not PG tsvector/tsquery
||YesSereneDB full-text search (inverted index), not PG tsvector/tsquery
&&YesSereneDB full-text search (inverted index), not PG tsvector/tsquery
!!YesSereneDB full-text search (inverted index), not PG tsvector/tsquery
<->No
@>No
<@No
array_to_tsvectorNo
get_current_ts_configNo
lengthNo
numnodeNo
plainto_tsqueryYesSereneDB full-text search (inverted index), not PG tsvector/tsquery
phraseto_tsqueryYesSereneDB full-text search (inverted index), not PG tsvector/tsquery
websearch_to_tsqueryYesSereneDB full-text search (inverted index), not PG tsvector/tsquery
querytreeNo
setweightNo
stripNo
to_tsqueryYesSereneDB full-text search (inverted index), not PG tsvector/tsquery
to_tsvectorNo
json(b)_to_tsvectorNo
ts_deleteNo
ts_filterNo
ts_headlineNo
ts_rankNo
ts_rank_cdNo
ts_rewriteNo
tsquery_phraseYesSereneDB full-text search (inverted index), not PG tsvector/tsquery
tsvector_to_arrayNo
unnestNo
ts_debugNo
ts_lexizeYesSereneDB extension: works with CREATE TEXT SEARCH DICTIONARY
ts_parseNo
ts_token_typeNo
ts_statNo

UUID

FeatureSupport StateDetails
get_random_uuidYes
uuid_extract_timestampYes
uuid_extract_versionYes

XML

FeatureSupport StateDetails
xmltextNo
xmlcommentNo
xmlconcatNo
xmlelementNo
xmlforestNo
xmlpiNo
xmlrootNo
xmlaggNo
IS DOCUMENTNo
IS NOT DOCUMENTNo
XMLEXISTSNo
xml_is_well_formedNo
xpathNo
xpath_existsNo
XMLTABLENo

JSON

The PostgreSQL-named JSON functions below are largely unsupported. SereneDB instead provides JSON access through the JSON column type and SereneDB-style functions — json_extract, json_extract_string, json_keys, json_type, json_valid and more (see JSON Functions). The PostgreSQL accessor operators ->, ->>, #> and #>> are supported.

Binary JSON (jsonb) is not supported, so every jsonb_* function and the jsonb-only operators are unavailable.

FeatureSupport StateDetails
->Yes
->>Yes
#>Yes
#>>Yes
@>NoUse json_contains()
<@NoUse json_contains()
?NoUse json_exists()
?|NoUse json_exists()
?&NoUse json_exists()
||Yes
-No
#-No
@?NoUse json_exists()
@@NoNo JSON-path predicate
to_jsonYes
to_jsonbNoUse to_json
array_to_jsonYes
json_arrayYes
row_to_jsonYes
json_build_arrayNoUse json_array()
jsonb_build_arrayNo
json_build_objectNoUse json_object(key, value)
jsonb_build_objectNo
json_objectPartialRequires even arg count; SereneDB json_object(key, value)
jsonb_objectNo
IS JSONNoUse json_valid()
json_array_elementsNoUse json_each()
jsonb_array_elementsNo
json_array_elements_textNoUse json_extract_string()
jsonb_array_elements_textNo
json_array_lengthYes
jsonb_array_lengthNoUse json_array_length
json_eachYes
jsonb_eachNo
json_each_textNoUse json_each()
jsonb_each_textNo
json_object_keysNoUse json_keys()
jsonb_object_keysNo
json_populate_recordNoUse json_transform()
jsonb_populate_recordNo
json_populate_recordsetNoUse json_transform()
jsonb_populate_recordsetNo
json_to_recordNoUse json_transform()
jsonb_to_recordNo
json_to_recordsetNoUse json_transform()
jsonb_to_recordsetNo
jsonb_setNo
jsonb_set_laxNo
jsonb_insertNo
json_strip_nullsYes
jsonb_strip_nullsNoUse json_strip_nulls
json_extract_pathYesSingle-arg form needs a cast; multi-arg works
json_extract_path_textYesSingle-arg form needs a cast; multi-arg works
json_pathNoNo jsonpath type

Sequence Manipulation

FeatureSupport StateDetails
nextvalYes
setvalYes
currvalYes
lastvalNo

Conditional

FeatureSupport StateDetails
CASEYes
COALESCEYes
NULLIFYes
GREATESTYes
LEASTYes

Array

FeatureSupport StateDetails
@>Yes
<@Yes
&&Yes
||YesNot for multidimensional
array_appendYes
array_catYes
array_dimsYes
array_fillNo
array_lowerYes
array_ndimsYes
array_positionYes
array_positionsYes
array_prependYes
array_removeYes
array_replaceYes
array_sampleNo
array_shuffleNo
array_to_stringYes
array_upperYes
array_cardinalityYesvia cardinality()
trim_arrayYes
unnestYesNo multi-array expansion

Range

FeatureSupport StateDetails
@>No
<@No
&&No
<<No
>>No
&<No
&>No
-| NoNo
+No
*No
-No
lowerNo
upperNo
isemptyNo
lower_incNo
upper_incNo
lower_infNo
upper_infNo
range_mergeNo
multirangeNo
unnestNo

Aggregate Functions

Generic
FeatureSupport StateDetails
any_valueYesAggregate functions
array_aggYesAggregate functions
avgYesAggregate functions
bit_andYesAggregate functions
bit_orYesAggregate functions
bit_xorYesAggregate functions
bool_andYesAggregate functions
bool_orYesAggregate functions
count(*)YesAggregate functions
count("any")YesAggregate functions
json(b)_aggNo
json(b)_objectaggNo
json(b)_object_aggNo
json_arrayaggNo
maxYesAggregate functions
minYesAggregate functions
range(_intersect)_aggNo
string_aggYes
sumYesAggregate functions
xmlaggNo
Statistical
FeatureSupport StateDetails
corrYes
covar_popYes
covar_sampYes
regr_avgxYes
regr_avgyYes
regr_countYes
regr_interceptYes
regr_r2Yes
regr_slopeYes
regr_sxxYes
regr_sxyYes
regr_syyYes
stddevYesAggregate functions
stddev_popYesAggregate functions
stddev_sampYesAggregate functions
varianceYesAggregate functions
var_popYesAggregate functions
var_sampYesAggregate functions
Ordered-Set
FeatureSupport StateDetails
modeYes
percentile_contYes
percentile_discYes

Window

FeatureSupport StateDetails
row_numberYes
rankYes
dense_rankYes
percent_rankYes
cume_distYes
ntileYes
lagYes
leadYes
first_valueYes
last_valueYes
nth_valueYes

Subquery

FeatureSupport StateDetails
EXISTSYes
INYes
NOT INYes
ANY/SOMEYes
ALLYes

Array Comparison

FeatureSupport StateDetails
EXISTSYes
INYes
NOT INYes
ANY/SOMEYes
ALLYes

Set Returning

FeatureSupport StateDetails
generate_seriesYes
generate_subscriptYes

Behavioral Differences from PostgreSQL

Even where a feature is supported, SereneDB intentionally diverges from PostgreSQL in a few places. SereneDB's SQL dialect closely follows PostgreSQL conventions; the exceptions are listed below.

Floating-Point Arithmetic

SereneDB and PostgreSQL handle floating-point arithmetic differently for division by zero. SereneDB conforms to the IEEE Standard for Floating-Point Arithmetic (IEEE 754) for both division by zero and operations involving infinity values. PostgreSQL returns an error for division by zero but aligns with IEEE 754 for handling infinity values. In SereneDB:

Query
SELECT 1.0 / 0.0 AS x;
SELECT 0.0 / 0.0 AS x;
SELECT -1.0 / 0.0 AS x;
SELECT 'Infinity'::FLOAT / 'Infinity'::FLOAT AS x;
SELECT 1.0 / 'Infinity'::FLOAT AS x;
SELECT 'Infinity'::FLOAT - 'Infinity'::FLOAT AS x;
SELECT 'Infinity'::FLOAT - 1.0 AS x;
Result
 x---------- Infinity
 x----- NaN
 x----------- -Infinity
 x----- NaN
 x--- 0
 x----- NaN
 x---------- Infinity

On an attached PostgreSQL, the same division by zero raises an error instead (operations involving infinity follow IEEE 754 on both engines):

Query
-- PostgreSQL raises an error on division by zero (SereneDB returns Infinity per IEEE 754)SELECT * FROM postgres_query('pg', 'SELECT 1.0 / 0.0');
Result
db error: ERROR: Failed to prepare COPY "COPY (SELECT "?column?" FROM (SELECT 1.0 / 0.0) AS __unnamed_subquery ) TO STDOUT (FORMAT "binary");": ERROR:  division by zero

Integer Division Operator (//)

Integer division itself behaves exactly like PostgreSQL: when both operands are integers, / performs integer division, so both engines return 0 here:

Query
SELECT 1 / 2 AS x;
Result
 x--- 0

The divergence is that SereneDB additionally accepts the // operator as an explicit integer-division spelling, which PostgreSQL does not provide (on PostgreSQL // raises operator does not exist):

Query
SELECT 1 // 2 AS x;
Result
 x--- 0

This returns 0.

UNION of Boolean and Integer Values

The following query fails in PostgreSQL but successfully completes in SereneDB:

Query
SELECT true AS xUNIONSELECT 2;
Result
 x--- 1 2

On an attached PostgreSQL, the same query raises an error:

Query
-- PostgreSQL rejects a UNION of boolean and integerSELECT * FROM postgres_query('pg', 'SELECT true UNION SELECT 1');
Result
db error: ERROR: Failed to prepare query "SELECT true UNION SELECT 1": ERROR:  UNION types boolean and integer cannot be matchedLINE 1: SELECT true UNION SELECT 1                                 ^

SereneDB instead performs an enforced cast and completes the query, returning the rows shown above.

Implicit Casting on Equality Checks

SereneDB performs implicit casting on equality checks, e.g., converting strings to numeric and boolean values. Therefore, there are several instances where PostgreSQL throws an error while SereneDB successfully computes the result. Each expression below is evaluated on both engines — the PostgreSQL column runs it on an attached PostgreSQL via postgres_query, the SereneDB column evaluates it locally:

Query
ATTACH 'host=localhost port=5432 dbname=mydb user=postgres' AS pg (TYPE postgres);
SELECT '''1.1'' = 1'     AS "Expression", 'error'                                                              AS "PostgreSQL", COALESCE(TRY(('1.1' = 1)::text),     'error') AS "SereneDB"UNION ALL SELECT '''1.1'' = 1.1',  (SELECT r FROM postgres_query('pg', 'SELECT (''1.1'' = 1.1)::text r')),  COALESCE(TRY(('1.1' = 1.1)::text),  'error')UNION ALL SELECT '1 = 1.1',        (SELECT r FROM postgres_query('pg', 'SELECT (1 = 1.1)::text r')),         COALESCE(TRY((1 = 1.1)::text),      'error')UNION ALL SELECT 'true = ''true''',(SELECT r FROM postgres_query('pg', 'SELECT (true = ''true'')::text r')), COALESCE(TRY((true = 'true')::text),'error')UNION ALL SELECT 'true = 1',       'error',                                                                  COALESCE(TRY((true = 1)::text),     'error')UNION ALL SELECT '''true'' = 1',   'error',                                                                  COALESCE(TRY(('true' = 1)::text),   'error');
Result
 Expression    | PostgreSQL | SereneDB---------------+------------+---------- '1.1' = 1     | error      | true '1.1' = 1.1   | true       | true 1 = 1.1       | false      | false true = 'true' | true       | true true = 1      | error      | true 'true' = 1    | error      | error

Case Sensitivity for Quoted Identifiers

PostgreSQL is case-insensitive. The way PostgreSQL achieves case insensitivity is by lowercasing unquoted identifiers within SQL, whereas quoting preserves case, e.g., the following command creates a table named mytable but tries to query for MyTaBLe because quotes preserve the case.

Query
CREATE TABLE MyTaBLe (x INTEGER);
SELECT * FROM "MyTaBLe";
Result
db error: ERROR: Table with name MyTaBLe does not exist!Did you mean "mytable"?

PostgreSQL does not only treat quoted identifiers as case-sensitive; it treats all identifiers as case-sensitive, e.g., this also does not work:

Query
CREATE TABLE "PreservedCase" (x INTEGER);
SELECT * FROM PreservedCase;
Result
db error: ERROR: Table with name preservedcase does not exist!Did you mean "PreservedCase"?

Therefore, case-insensitivity in PostgreSQL only works if you never use quoted identifiers with different cases.

For SereneDB, this behavior was problematic when interfacing with other tools (e.g., Parquet, Pandas) that are case-sensitive by default – since all identifiers would be lowercased all the time. Therefore, SereneDB achieves case insensitivity by making identifiers fully case insensitive throughout the system but preserving their case.

In SereneDB, the scripts above complete successfully:

Query
CREATE TABLE MyOtheRTaBLe (x INTEGER);
SELECT * FROM "MyOtheRTaBLe";
CREATE TABLE "PreservedCase" (x INTEGER);
SELECT * FROM PreservedCase;
SELECT tablename FROM pg_tables;
Result
x
x
 tablename--------------- MyOtheRTaBLe PreservedCase

PostgreSQL's behavior of lowercasing identifiers is accessible using the preserve_identifier_case option:

Query
SET preserve_identifier_case = false;
CREATE TABLE MyTaBLe (x INTEGER);
SELECT tbl FROM duckdb_tables();
Result
 tbl--------- mytable

However, the case insensitive matching in the system for identifiers cannot be turned off.

Using Double Equality Sign for Comparison

SereneDB supports both = and == for equality comparison, while PostgreSQL only supports =.

Query
SELECT 1 == 1 AS t;
Result
 t--- t

SereneDB returns true, while on an attached PostgreSQL the same query errors (== is not a PostgreSQL operator):

Query
-- PostgreSQL has no == operatorSELECT * FROM postgres_query('pg', 'SELECT 1 == 1');
Result
db error: ERROR: Failed to prepare query "SELECT 1 == 1": ERROR:  operator does not exist: integer == integerLINE 1: SELECT 1 == 1                 ^HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.

Note that the use of == is not encouraged due to its limited portability.

Vacuuming Tables

In PostgreSQL, the VACUUM statement garbage collects tables and analyzes tables. In SereneDB, the VACUUM statement is only used to rebuild statistics. To reclaim space, use the CHECKPOINT statement or compact the database by creating a fresh copy with the COPY FROM DATABASE statement.

Strings

SereneDB escapes characters such as ' when it serializes strings inside nested data structures — for example, casting a list that contains a quote to text:

Query
SELECT (['a''b'])::VARCHAR AS escaped;
Result
 escaped----------- ['a\\'b']

PostgreSQL's array-to-text conversion leaves the quote unescaped instead:

Query
-- PostgreSQL's array-to-text leaves the quote unescaped (compare with SereneDB's escaped list cast)SELECT r AS "PostgreSQL" FROM postgres_query('pg', 'SELECT (ARRAY[''a''''b''])::text r');
Result
 PostgreSQL------------ {a'b}

Functions

regexp_extract Function

Unlike PostgreSQL's regexp_substr function, SereneDB's regexp_extract returns empty strings instead of NULLs when there is no match:

Query
SELECT regexp_extract('abc', 'x') AS result, regexp_extract('abc', 'x') IS NULL AS is_null;
Result
 result  | is_null---------+--------- (empty) | f

to_date Function

SereneDB does not support the to_date PostgreSQL date formatting function. Instead, please use the strptime function:

Query
SELECT to_date('2024-01-01', 'YYYY-MM-DD');
Result
db error: ERROR: Scalar Function with name to_date does not exist!Did you mean "to_days"?

date_part Function

Most parts extracted by the date_part function are returned as integers. Since there are no infinite integer values in SereneDB, NULLs are returned for infinite timestamps:

Query
SELECT date_part('year', TIMESTAMP '2021-08-03 11:59:44.123456') AS finite,       date_part('year', 'infinity'::TIMESTAMP) AS infinite;
Result
 finite | infinite--------+----------   2021 | NULL

Resolution of Type Names in the Schema

For CREATE TABLE statements, SereneDB attempts to resolve type names in the schema where a table is created. For example:

Query
CREATE SCHEMA myschema;
CREATE TYPE myschema.mytype AS ENUM ('as', 'df');
CREATE TABLE myschema.mytable (v mytype);

On an attached PostgreSQL, the same CREATE TABLE errors because PostgreSQL does not resolve the unqualified type name in the table's schema:

Query
-- PostgreSQL does not resolve the unqualified type name (SereneDB resolves it in the table's schema)CALL postgres_execute('pg', 'CREATE TABLE mytable (v mytype)');
Result
db error: ERROR: Failed to execute query "BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;CREATE TABLE mytable (v mytype)": ERROR:  type "mytype" does not existLINE 2: CREATE TABLE mytable (v mytype)                                ^

SereneDB runs the statement and creates the table successfully, confirmed by the following query:

Query
DESCRIBE myschema.mytable;
Result
 column_name | column_type | null | key  | default | extra-------------+-------------+------+------+---------+------- v           | mytype      | YES  | NULL | NULL    | NULL

Exploiting Functional Dependencies for GROUP BY

PostgreSQL can exploit functional dependencies, such as i -> j in the following query, and runs it. SereneDB instead raises the error shown below:

Query
CREATE TABLE tbl (i INTEGER, j INTEGER, PRIMARY KEY (i));
SELECT jFROM tblGROUP BY i;
Result
db error: ERROR: column "j" must appear in the GROUP BY clause or must be part of an aggregate function.Either add it to the GROUP BY list, or use "ANY_VALUE(j)" if the exact value of "j" is not important.

To work around this, add the other attributes or use the GROUP BY ALL clause.