@@ -9,28 +9,41 @@ import {
99 toSyncRulesValue
1010} from '@powersync/service-sync-rules' ;
1111import * as pgwire from '@powersync/service-jpgwire' ;
12- import { JsonContainer } from '@powersync/service-jsonbig' ;
1312
1413interface BaseType {
1514 sqliteType : ( ) => SqliteValueType ;
1615}
1716
17+ /** A type natively supported by {@link pgwire.PgType.decode}. */
1818interface BuiltinType extends BaseType {
1919 type : 'builtin' ;
2020 oid : number ;
2121}
2222
23+ /**
24+ * An array type.
25+ */
2326interface ArrayType extends BaseType {
2427 type : 'array' ;
2528 innerId : number ;
2629 separatorCharCode : number ;
2730}
2831
32+ /**
33+ * A domain type, like `CREATE DOMAIN api.rating_value AS FLOAT CHECK (VALUE BETWEEN 0 AND 5);`
34+ *
35+ * This type gets decoded and synced as the inner type (`FLOAT` in the example above).
36+ */
2937interface DomainType extends BaseType {
3038 type : 'domain' ;
3139 innerId : number ;
3240}
3341
42+ /**
43+ * A composite type as created by `CREATE TYPE AS`.
44+ *
45+ * These types are encoded as a tuple of values, so we recover attribute names to restore them as a JSON object.
46+ */
3447interface CompositeType extends BaseType {
3548 type : 'composite' ;
3649 members : { name : string ; typeId : number } [ ] ;
@@ -81,6 +94,12 @@ class CustomTypeValue extends CustomSqliteValue {
8194 }
8295}
8396
97+ /**
98+ * A registry of custom types.
99+ *
100+ * These extend the builtin decoding behavior in {@link pgwire.PgType.decode} for user-defined types like `DOMAIN`s or
101+ * composite types.
102+ */
84103export class CustomTypeRegistry {
85104 private readonly byOid : Map < number , KnownType > ;
86105
@@ -89,6 +108,7 @@ export class CustomTypeRegistry {
89108
90109 for ( const builtin of Object . values ( pgwire . PgTypeOid ) ) {
91110 if ( typeof builtin == 'number' ) {
111+ // We need to know the SQLite type of builtins to implement CustomSqliteValue.sqliteType for DOMAIN types.
92112 let sqliteType : SqliteValueType ;
93113 switch ( builtin ) {
94114 case pgwire . PgTypeOid . TEXT :
@@ -168,12 +188,6 @@ export class CustomTypeRegistry {
168188 return pgwire . PgType . decode ( raw , oid ) ;
169189 case 'domain' :
170190 return this . decodeWithCustomTypes ( raw , resolved . innerId ) ;
171- case 'array' :
172- return pgwire . decodeArray ( {
173- source : raw ,
174- decodeElement : ( source ) => this . decodeWithCustomTypes ( source , resolved . innerId ) ,
175- delimiterCharCode : resolved . separatorCharCode
176- } ) ;
177191 case 'composite' : {
178192 const parsed : [ string , any ] [ ] = [ ] ;
179193
@@ -196,6 +210,28 @@ export class CustomTypeRegistry {
196210
197211 return Object . fromEntries ( parsed ) ;
198212 }
213+ case 'array' : {
214+ // Nornalize "array of array of T" types into just "array of T", because Postgres arrays are natively multi-
215+ // dimensional. This may be required when we have a DOMAIN wrapper around an array followed by another array
216+ // around that domain.
217+ let innerId = resolved . innerId ;
218+ while ( true ) {
219+ const resolvedInner = this . lookupType ( innerId ) ;
220+ if ( resolvedInner . type == 'domain' ) {
221+ innerId = resolvedInner . innerId ;
222+ } else if ( resolvedInner . type == 'array' ) {
223+ innerId = resolvedInner . innerId ;
224+ } else {
225+ break ;
226+ }
227+ }
228+
229+ return pgwire . decodeArray ( {
230+ source : raw ,
231+ decodeElement : ( source ) => this . decodeWithCustomTypes ( source , innerId ) ,
232+ delimiterCharCode : resolved . separatorCharCode
233+ } ) ;
234+ }
199235 }
200236 }
201237
@@ -217,6 +253,8 @@ export class CustomTypeRegistry {
217253
218254 decodeDatabaseValue ( value : string , oid : number ) : DatabaseInputValue {
219255 const resolved = this . lookupType ( oid ) ;
256+ // For backwards-compatibility, some types are only properly parsed with a compatibility option. Others are synced
257+ // in the raw text representation by default, and are only parsed as JSON values when necessary.
220258 if ( this . isParsedWithoutCustomTypesSupport ( resolved ) ) {
221259 return pgwire . PgType . decode ( value , oid ) ;
222260 } else {
0 commit comments