Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dialect aware Postgres types not working for .drift files #2922

Open
Leylan24 opened this issue Mar 14, 2024 · 7 comments
Open

Dialect aware Postgres types not working for .drift files #2922

Leylan24 opened this issue Mar 14, 2024 · 7 comments
Labels
bug Something isn't working package-drift_dev Affects the drift_dev package

Comments

@Leylan24
Copy link
Contributor

I have a custom sql type that I created for Postgres to Sqlite databases. The class looks like this:

class FallbackTimestampWithoutTimezoneType
    implements CustomSqlType<PgDateTime> {
  const FallbackTimestampWithoutTimezoneType();

  @override
  String mapToSqlLiteral(PgDateTime dartValue) {
    return "'${dartValue.dateTime.toUtc().toIso8601String()}'";
  }

  @override
  Object mapToSqlParameter(PgDateTime? dartValue) {
    final DateTime defaultValue = DateTime.utc(1900, 1, 1);
    final DateTime valueToUse = dartValue?.dateTime ?? defaultValue;
    return valueToUse.toUtc().toIso8601String();
  }

  @override
  PgDateTime read(Object fromSql) {
    if (fromSql is DateTime) {
      return PgDateTime(fromSql);
    } else if (fromSql is String) {
      return PgDateTime(DateTime.parse(fromSql));
    } else {
      throw const FormatException('Invalid format for timestamp data');
    }
  }

  @override
  String sqlTypeName(GenerationContext context) {
    return 'TEXT';
  }
}

const timestampWithoutTimezoneType = DialectAwareSqlType<PgDateTime>.via(
  fallback: FallbackTimestampWithoutTimezoneType(),
  overrides: {
    SqlDialect.postgres: PgTypes.timestampNoTimezone,
  },
);

when i use it in a .dart defined context it works perfectly.

  TimestampColumn get date =>
      customType(timestampWithoutTimezoneType).named('date').nullable()();

but if i try to implement this in a drift file it does not generate correctly.

CREATE TABLE "test" (
	id int4 NOT NULL PRIMARY KEY,
	date`const timestampWithoutTimezoneType() ` NULL
);
@Leylan24
Copy link
Contributor Author

when its a drift file it generates

  late final GeneratedColumn<double> date=
      GeneratedColumn<double>('date', aliasedName, true,
          type: DriftSqlType.double,
          requiredDuringInsert: false,
          $customConstraints: 'NULL');

@simolus3
Copy link
Owner

I'll take a look. But to be sure, are you importing the Dart file into the drift file? You also need to remove the const and () in the definition since you're just referencing an existing constant, does date `timestampWithoutTimezoneType` NULL work?

@Leylan24
Copy link
Contributor Author

That did seem to work.

  late final GeneratedColumn<PgDateTime> date=
      GeneratedColumn<PgDateTime>('date', aliasedName, true,
          type: timestampWithoutTimezoneType,
          requiredDuringInsert: false,
          $customConstraints: 'NULL');

however there was some errors in the drift log even though it did succeed:

Internal error while deserializing DriftElementId(package:pepkor_db/src/tables/testTable.drift, testTable): type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast at
#0      ElementDeserializer._readColumnType (package:drift_dev/src/analysis/serializer.dart:742:55)
#1      ElementDeserializer._readColumn (package:drift_dev/src/analysis/serializer.dart:756:17)
#2      ElementDeserializer._readDriftElement (package:drift_dev/src/analysis/serializer.dart:488:19)
<asynchronous suspension>
#3      ElementDeserializer.readDriftElement (package:drift_dev/src/analysis/serializer.dart:453:22)
<asynchronous suspension>
#4      DriftResolver._restoreOrResolve (package:drift_dev/src/analysis/resolver/resolver.dart:45:16)
<asynchronous suspension>
#5      DriftResolver.resolveEntrypoint (package:drift_dev/src/analysis/resolver/resolver.dart:39:12)
<asynchronous suspension>
#6      DriftAnalysisDriver.resolveElement (package:drift_dev/src/analysis/driver/driver.dart:247:16)
<asynchronous suspension>
#7      DriftAnalysisDriver._analyzeLocalElements (package:drift_dev/src/analysis/driver/driver.dart:232:7)
<asynchronous suspension>
#8      DriftAnalysisDriver.resolveElements (package:drift_dev/src/analysis/driver/driver.dart:299:5)
<asynchronous suspension>
#9      FileAnalyzer.runAnalysisOn (package:drift_dev/src/analysis/resolver/file_analysis.dart:42:13)
<asynchronous suspension>
#10     DriftAnalysisDriver.fullyAnalyze (package:drift_dev/src/analysis/driver/driver.dart:312:22)
<asynchronous suspension>
#11     _DriftBuildRun._analyze (package:drift_dev/src/backends/build/drift_builder.dart:178:20)
<asynchronous suspension>
#12     _DriftBuildRun.run (package:drift_dev/src/backends/build/drift_builder.dart:156:9)
<asynchronous suspension>
#13     DriftBuilder.build (package:drift_dev/src/backends/build/drift_builder.dart:106:5)
<asynchronous suspension>
#14     runBuilder.buildForInput (package:build/src/generate/run_builder.dart:83:7)
<asynchronous suspension>
#15     Future.wait.<anonymous closure> (dart:async/future.dart:518:21)
<asynchronous suspension>
#16     scopeLogAsync.<anonymous closure> (package:build/src/builder/logging.dart:32:40)
<asynchronous suspension>

package:drift_dev/src/analysis/serializer.dart 462:7              ElementDeserializer.readDriftElement
package:drift_dev/src/analysis/resolver/resolver.dart 45:16       DriftResolver._restoreOrResolve
package:drift_dev/src/analysis/resolver/resolver.dart 39:12       DriftResolver.resolveEntrypoint
package:drift_dev/src/analysis/driver/driver.dart 247:16          DriftAnalysisDriver.resolveElement
package:drift_dev/src/analysis/driver/driver.dart 232:7           DriftAnalysisDriver._analyzeLocalElements
package:drift_dev/src/analysis/driver/driver.dart 299:5           DriftAnalysisDriver.resolveElements
package:drift_dev/src/analysis/resolver/file_analysis.dart 42:13  FileAnalyzer.runAnalysisOn
package:drift_dev/src/analysis/driver/driver.dart 312:22          DriftAnalysisDriver.fullyAnalyze
package:drift_dev/src/backends/build/drift_builder.dart 178:20    _DriftBuildRun._analyze
package:drift_dev/src/backends/build/drift_builder.dart 156:9     _DriftBuildRun.run
package:drift_dev/src/backends/build/drift_builder.dart 106:5     DriftBuilder.build

@simolus3
Copy link
Owner

That's odd. There should be a Dart file in .dart_tool/build/generated/<yourpackage>/path/to/drift/file.drift_module.json. Can you share that file? Also, are you on the latest drift version (the line numbers in the stack trace are slightly off but I think I see where the error is coming from, I just don't understand why yet).

@simolus3 simolus3 added bug Something isn't working package-drift_dev Affects the drift_dev package labels Mar 14, 2024
@Leylan24
Copy link
Contributor Author

Leylan24 commented Mar 14, 2024

Okay i will need to give you more context.

I still get the error but the build_runner succeeds, this is the file you requested.

branch_setup.drift.drift_module.json

This is the .drift file:

import '../convertors/timestamp/pg_timestamp.dart';
import '../convertors/json/pg_json.dart';
import '../convertors/functions/pg_current_user.dart';
import 'package:sqlite_postgresql_connector/sqlite_postgresql_connector.dart';
CREATE TABLE "central.branch_setup" (
	branch_id int4 NOT NULL PRIMARY KEY,
	curr_id int4 NOT NULL,
	menu_ver_id int4 NULL,
	updated_on `timestampWithoutTimezoneType` NOT NULL,
	updated_by varchar(15) NOT NULL MAPPED BY `const PgCurrentUserConverter()`,
	created_by varchar(15) NOT NULL MAPPED BY `const PgCurrentUserConverter()`,
	created_on `timestampWithoutTimezoneType` NOT NULL,
	branch_setup_cashup_attr `jsonType` NULL,
	lang_id int4 NULL
);

The custom sql type converters I am using are:

import 'package:drift/drift.dart';
import 'package:drift_postgres/drift_postgres.dart';


// Fallback for SQLite since it does not support TIMESTAMP WITHOUT TIME ZONE.
class FallbackTimestampWithoutTimezoneType
    implements CustomSqlType<PgDateTime> {
  const FallbackTimestampWithoutTimezoneType();

  @override
  String mapToSqlLiteral(PgDateTime dartValue) {
    return "'${dartValue.dateTime.toUtc().toIso8601String()}'";
  }

  @override
  Object mapToSqlParameter(PgDateTime? dartValue) {
    final DateTime defaultValue = DateTime.utc(1900, 1, 1);
    final DateTime valueToUse = dartValue?.dateTime ?? defaultValue;
    return valueToUse.toUtc().toIso8601String();
  }

  @override
  PgDateTime read(Object fromSql) {
    if (fromSql is DateTime) {
      return PgDateTime(fromSql);
    } else if (fromSql is String) {
      return PgDateTime(DateTime.parse(fromSql));
    } else {
      throw const FormatException('Invalid format for timestamp data');
    }
  }

  @override
  String sqlTypeName(GenerationContext context) {
    return 'TEXT';
  }
}

const timestampWithoutTimezoneType = DialectAwareSqlType<PgDateTime>.via(
  fallback: FallbackTimestampWithoutTimezoneType(),
  overrides: {
    SqlDialect.postgres: PgTypes.timestampNoTimezone,
  },
);

import 'package:drift/drift.dart';
import 'package:drift_postgres/drift_postgres.dart';

class _FallbackNumericType implements CustomSqlType<String> {
  const _FallbackNumericType();

  @override
  String sqlTypeName(GenerationContext context) => 'TEXT';

  @override
  String mapToSqlLiteral(String dartValue) {
    // TODO: implement mapToSqlLiteral
    throw UnimplementedError();
  }

  @override
  Object mapToSqlParameter(String dartValue) {
    // TODO: implement mapToSqlParameter
    throw UnimplementedError();
  }

  @override
  String read(Object fromSql) {
    // TODO: implement read
    throw UnimplementedError();
  } // or 'TEXT' if you prefer string representation
}

const jsonType = DialectAwareSqlType.via(
  fallback: _FallbackNumericType(),
  overrides: {
    SqlDialect.postgres: PgTypes.jsonb,
  },
);

I am still busy implementing the jsonType converter just thought if you had all the information it would help. The above example i created smaller tables to try and simulate the problem

@simolus3
Copy link
Owner

Thanks! The JSON looks correct to me though, and I can't reproduce the crash.

Internal error while deserializing DriftElementId(package:pepkor_db/src/tables/testTable.drift, testTable): type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast at

Is this the same table that you've renamed in the meantime? If not, do you still have testTable sources available so I could take a look at them as well?

@Leylan24
Copy link
Contributor Author

I dont have the table but the same error gets produced from this table

import '../convertors/timestamp/pg_timestamp.dart';
import '../convertors/json/pg_json.dart';
import '../convertors/functions/pg_current_user.dart';
import 'package:sqlite_postgresql_connector/sqlite_postgresql_connector.dart';
CREATE TABLE "central.branch_setup" (
	branch_id int4 NOT NULL PRIMARY KEY,
	curr_id int4 NOT NULL,
	menu_ver_id int4 NULL,
	updated_on `timestampWithoutTimezoneType` NOT NULL,
	updated_by varchar(15) NOT NULL MAPPED BY `const PgCurrentUserConverter()`,
	created_by varchar(15) NOT NULL MAPPED BY `const PgCurrentUserConverter()`,
	created_on `timestampWithoutTimezoneType` NOT NULL,
	branch_setup_cashup_attr `jsonType` NULL,
	lang_id int4 NULL
);

Internal error while deserializing DriftElementId(package:pepkor_db/src/tables/branch_setup.drift, central.branch_setup): type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast at
#0      ElementDeserializer._readColumnType (package:drift_dev/src/analysis/serializer.dart:742:55)
#1      ElementDeserializer._readColumn (package:drift_dev/src/analysis/serializer.dart:756:17)
#2      ElementDeserializer._readDriftElement (package:drift_dev/src/analysis/serializer.dart:488:19)
<asynchronous suspension>
#3      ElementDeserializer.readDriftElement (package:drift_dev/src/analysis/serializer.dart:453:22)
<asynchronous suspension>
#4      DriftResolver._restoreOrResolve (package:drift_dev/src/analysis/resolver/resolver.dart:45:16)
<asynchronous suspension>
#5      DriftResolver.resolveEntrypoint (package:drift_dev/src/analysis/resolver/resolver.dart:39:12)
<asynchronous suspension>
#6      DriftAnalysisDriver.resolveElement (package:drift_dev/src/analysis/driver/driver.dart:247:16)
<asynchronous suspension>
#7      DriftAnalysisDriver._analyzeLocalElements (package:drift_dev/src/analysis/driver/driver.dart:232:7)
<asynchronous suspension>
#8      DriftAnalysisDriver.resolveElements (package:drift_dev/src/analysis/driver/driver.dart:299:5)
<asynchronous suspension>
#9      FileAnalyzer.runAnalysisOn (package:drift_dev/src/analysis/resolver/file_analysis.dart:42:13)
<asynchronous suspension>
#10     DriftAnalysisDriver.fullyAnalyze (package:drift_dev/src/analysis/driver/driver.dart:312:22)
<asynchronous suspension>
#11     _DriftBuildRun._analyze (package:drift_dev/src/backends/build/drift_builder.dart:178:20)
<asynchronous suspension>
#12     _DriftBuildRun.run (package:drift_dev/src/backends/build/drift_builder.dart:156:9)
<asynchronous suspension>
#13     DriftBuilder.build (package:drift_dev/src/backends/build/drift_builder.dart:106:5)
<asynchronous suspension>
#14     runBuilder.buildForInput (package:build/src/generate/run_builder.dart:83:7)
<asynchronous suspension>
#15     Future.wait.<anonymous closure> (dart:async/future.dart:518:21)
<asynchronous suspension>
#16     scopeLogAsync.<anonymous closure> (package:build/src/builder/logging.dart:32:40)
<asynchronous suspension>

package:drift_dev/src/analysis/serializer.dart 462:7              ElementDeserializer.readDriftElement
package:drift_dev/src/analysis/resolver/resolver.dart 45:16       DriftResolver._restoreOrResolve
package:drift_dev/src/analysis/resolver/resolver.dart 39:12       DriftResolver.resolveEntrypoint
package:drift_dev/src/analysis/driver/driver.dart 247:16          DriftAnalysisDriver.resolveElement
package:drift_dev/src/analysis/driver/driver.dart 232:7           DriftAnalysisDriver._analyzeLocalElements
package:drift_dev/src/analysis/driver/driver.dart 299:5           DriftAnalysisDriver.resolveElements
package:drift_dev/src/analysis/resolver/file_analysis.dart 42:13  FileAnalyzer.runAnalysisOn
package:drift_dev/src/analysis/driver/driver.dart 312:22          DriftAnalysisDriver.fullyAnalyze
package:drift_dev/src/backends/build/drift_builder.dart 178:20    _DriftBuildRun._analyze
package:drift_dev/src/backends/build/drift_builder.dart 156:9     _DriftBuildRun.run
package:drift_dev/src/backends/build/drift_builder.dart 106:5     DriftBuilder.build

I can try and reproduce the table if you need it ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working package-drift_dev Affects the drift_dev package
Projects
None yet
Development

No branches or pull requests

2 participants