loopbackio/loopback-connector-postgresql

Default values in Models properties totally being ignored when using migrateschema.

tarangill opened this issue · 1 comments

Steps to reproduce

This is my model:

import {Entity, model, property} from '@loopback/repository';

@model({
  settings: {
  }
})
export class Sound extends Entity {

  @property({
    type: 'string',
    id: true,
    required: true,
    length: 17,
    postgresql:
    {
       "dataType":"VARCHAR",
       "dataLength":17,
       "nullable":"N"
    }
  })
  sound_id: string;

  @property({
    type: 'string',
    postgresql:
    {
       "dataType":"VARCHAR",
       "dataLength": 256,
       "nullable":"y",
       default: "heelloi"
    },
  })
  title?: string;

  @property({
    type: 'string',
    postgresql:
    {
       "dataType":"VARCHAR",
       "dataLength": 1024,
       "nullable":"n"
    }
  })
  url: string;

  @property({
    postgresql: {
      dataType: 'TIMESTAMP WITH TIME ZONE',
      default: 'CURRENT_TIMESTAMP'
    },
    "index": true
  })
  created_at: string;

  constructor(data?: Partial<Sound>) {
    super(data);
  }
}

export interface SoundRelations {
  // describe navigational properties here
}

export type SoundtWithRelations = Sound & SoundRelations;

Current Behavior

When I use app.migrateSchema(['Sound') to automatically create the table, this is what I get from \d+ sound in ysqlsh:

                                              Table "public.sound"
   Column   |           Type           | Collation | Nullable | Default | Storage  | Stats target | Description 
------------+--------------------------+-----------+----------+---------+----------+--------------+-------------
 sound_id   | character varying(17)    |           | not null |         | extended |              | 
 title      | character varying(256)   |           |          |         | extended |              | 
 url        | character varying(1024)  |           |          |         | extended |              | 
 created_at | timestamp with time zone |           |          |         | plain    |              | 
Indexes:
    "sound_pkey" PRIMARY KEY, lsm (sound_id HASH)
    "sound_created_at_idx" lsm (created_at HASH)

Notice how there is no default values for title and created at?

this is the output of the npm start run with DEBUG=loopback:connector:*:

  loopback:connector:postgresql:data {"command":"SELECT","rowCount":0,"oid":null,"rows":[],"fields":[{"name":"column","tableID":13116,"columnID":4,"dataTypeID":1043,"dataTypeSize":-1,"dataTypeModifier":-1,"format":"text"},{"name":"type","tableID":13116,"columnID":8,"dataTypeID":1043,"dataTypeSize":-1,"dataTypeModifier":-1,"format":"text"},{"name":"time_precision","tableID":13116,"columnID":14,"dataTypeID":23,"dataTypeSize":4,"dataTypeModifier":-1,"format":"text"},{"name":"nullable","tableID":13116,"columnID":7,"dataTypeID":1043,"dataTypeSize":-1,"dataTypeModifier":7,"format":"text"},{"name":"length","tableID":13116,"columnID":9,"dataTypeID":23,"dataTypeSize":4,"dataTypeModifier":-1,"format":"text"}],"_parsers":[null,null,null,null,null],"_types":{"_types":{"arrayParser":{},"builtins":{"BOOL":16,"BYTEA":17,"CHAR":18,"INT8":20,"INT2":21,"INT4":23,"REGPROC":24,"TEXT":25,"OID":26,"TID":27,"XID":28,"CID":29,"JSON":114,"XML":142,"PG_NODE_TREE":194,"SMGR":210,"PATH":602,"POLYGON":604,"CIDR":650,"FLOAT4":700,"FLOAT8":701,"ABSTIME":702,"RELTIME":703,"TINTERVAL":704,"CIRCLE":718,"MACADDR8":774,"MONEY":790,"MACADDR":829,"INET":869,"ACLITEM":1033,"BPCHAR":1042,"VARCHAR":1043,"DATE":1082,"TIME":1083,"TIMESTAMP":1114,"TIMESTAMPTZ":1184,"INTERVAL":1186,"TIMETZ":1266,"BIT":1560,"VARBIT":1562,"NUMERIC":1700,"REFCURSOR":1790,"REGPROCEDURE":2202,"REGOPER":2203,"REGOPERATOR":2204,"REGCLASS":2205,"REGTYPE":2206,"UUID":2950,"TXID_SNAPSHOT":2970,"PG_LSN":3220,"PG_NDISTINCT":3361,"PG_DEPENDENCIES":3402,"TSVECTOR":3614,"TSQUERY":3615,"GTSVECTOR":3642,"REGCONFIG":3734,"REGDICTIONARY":3769,"JSONB":3802,"REGNAMESPACE":4089,"REGROLE":4096}},"text":{},"binary":{}},"RowCtor":null,"rowAsArray":false} +0ms
  loopback:connector:postgresql SQL: SELECT t.relname AS "table", i.relname AS "name", am.amname AS "type", ix.indisprimary AS "primary", ix.indisunique AS "unique", ARRAY(SELECT pg_get_indexdef(ix.indexrelid, k + 1, true)   FROM generate_subscripts(ix.indkey, 1) AS k   ORDER BY k ) AS "keys", ARRAY(SELECT   CASE ix.indoption[k] & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END   FROM generate_subscripts(ix.indoption, 1) AS k   ORDER BY k ) AS "order" FROM pg_class t, pg_class i, pg_index ix, pg_am am, pg_namespace ns WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND i.relam = am.oid AND t.relkind='r' AND t.relname='sound' and (ns.oid = t.relnamespace and ns.nspname='public') +2s
  loopback:connector:postgresql:data {"command":"SELECT","rowCount":0,"oid":null,"rows":[],"fields":[{"name":"table","tableID":1259,"columnID":1,"dataTypeID":19,"dataTypeSize":64,"dataTypeModifier":-1,"format":"text"},{"name":"name","tableID":1259,"columnID":1,"dataTypeID":19,"dataTypeSize":64,"dataTypeModifier":-1,"format":"text"},{"name":"type","tableID":2601,"columnID":1,"dataTypeID":19,"dataTypeSize":64,"dataTypeModifier":-1,"format":"text"},{"name":"primary","tableID":2610,"columnID":6,"dataTypeID":16,"dataTypeSize":1,"dataTypeModifier":-1,"format":"text"},{"name":"unique","tableID":2610,"columnID":5,"dataTypeID":16,"dataTypeSize":1,"dataTypeModifier":-1,"format":"text"},{"name":"keys","tableID":0,"columnID":0,"dataTypeID":1009,"dataTypeSize":-1,"dataTypeModifier":-1,"format":"text"},{"name":"order","tableID":0,"columnID":0,"dataTypeID":1009,"dataTypeSize":-1,"dataTypeModifier":-1,"format":"text"}],"_parsers":[null,null,null,null,null,null,null],"_types":{"_types":{"arrayParser":{},"builtins":{"BOOL":16,"BYTEA":17,"CHAR":18,"INT8":20,"INT2":21,"INT4":23,"REGPROC":24,"TEXT":25,"OID":26,"TID":27,"XID":28,"CID":29,"JSON":114,"XML":142,"PG_NODE_TREE":194,"SMGR":210,"PATH":602,"POLYGON":604,"CIDR":650,"FLOAT4":700,"FLOAT8":701,"ABSTIME":702,"RELTIME":703,"TINTERVAL":704,"CIRCLE":718,"MACADDR8":774,"MONEY":790,"MACADDR":829,"INET":869,"ACLITEM":1033,"BPCHAR":1042,"VARCHAR":1043,"DATE":1082,"TIME":1083,"TIMESTAMP":1114,"TIMESTAMPTZ":1184,"INTERVAL":1186,"TIMETZ":1266,"BIT":1560,"VARBIT":1562,"NUMERIC":1700,"REFCURSOR":1790,"REGPROCEDURE":2202,"REGOPER":2203,"REGOPERATOR":2204,"REGCLASS":2205,"REGTYPE":2206,"UUID":2950,"TXID_SNAPSHOT":2970,"PG_LSN":3220,"PG_NDISTINCT":3361,"PG_DEPENDENCIES":3402,"TSVECTOR":3614,"TSQUERY":3615,"GTSVECTOR":3642,"REGCONFIG":3734,"REGDICTIONARY":3769,"JSONB":3802,"REGNAMESPACE":4089,"REGROLE":4096}},"text":{},"binary":{}},"RowCtor":null,"rowAsArray":false} +69ms
  loopback:connector:postgresql SQL: CREATE EXTENSION IF NOT EXISTS "uuid-ossp";CREATE SCHEMA "public" +70ms
  loopback:connector:postgresql error: schema "public" already exists
  loopback:connector:postgresql     at Parser.parseErrorMessage (/api-panga-tv/node_modules/pg-protocol/src/parser.ts:369:69)
  loopback:connector:postgresql     at Parser.handlePacket (/api-panga-tv/node_modules/pg-protocol/src/parser.ts:188:21)
  loopback:connector:postgresql     at Parser.parse (/api-panga-tv/node_modules/pg-protocol/src/parser.ts:103:30)
  loopback:connector:postgresql     at Socket.<anonymous> (/api-panga-tv/node_modules/pg-protocol/src/index.ts:7:48)
  loopback:connector:postgresql     at Socket.emit (node:events:527:28)
  loopback:connector:postgresql     at Socket.emit (node:domain:475:12)
  loopback:connector:postgresql     at addChunk (node:internal/streams/readable:315:12)
  loopback:connector:postgresql     at readableAddChunk (node:internal/streams/readable:289:9)
  loopback:connector:postgresql     at Socket.Readable.push (node:internal/streams/readable:228:10)
  loopback:connector:postgresql     at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
  loopback:connector:postgresql     at TCP.callbackTrampoline (node:internal/async_hooks:130:17) +16ms
  loopback:connector:sql Error: {"length":95,"name":"error","severity":"ERROR","code":"42P06","file":"pg_namespace.c","line":"63","routine":"NamespaceCreate"} "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";CREATE SCHEMA \"public\"" [] +0ms
  loopback:connector:postgresql SQL: CREATE TABLE "public"."sound" (
  "sound_id" VARCHAR(17) NOT NULL,
  "title" VARCHAR(256),
  "url" VARCHAR(1024),
  "created_at" TIMESTAMP WITH TIME ZONE,
  PRIMARY KEY("sound_id")
) +8ms
  loopback:connector:postgresql:data {"command":"CREATE","rowCount":null,"oid":null,"rows":[],"fields":[],"_types":{"_types":{"arrayParser":{},"builtins":{"BOOL":16,"BYTEA":17,"CHAR":18,"INT8":20,"INT2":21,"INT4":23,"REGPROC":24,"TEXT":25,"OID":26,"TID":27,"XID":28,"CID":29,"JSON":114,"XML":142,"PG_NODE_TREE":194,"SMGR":210,"PATH":602,"POLYGON":604,"CIDR":650,"FLOAT4":700,"FLOAT8":701,"ABSTIME":702,"RELTIME":703,"TINTERVAL":704,"CIRCLE":718,"MACADDR8":774,"MONEY":790,"MACADDR":829,"INET":869,"ACLITEM":1033,"BPCHAR":1042,"VARCHAR":1043,"DATE":1082,"TIME":1083,"TIMESTAMP":1114,"TIMESTAMPTZ":1184,"INTERVAL":1186,"TIMETZ":1266,"BIT":1560,"VARBIT":1562,"NUMERIC":1700,"REFCURSOR":1790,"REGPROCEDURE":2202,"REGOPER":2203,"REGOPERATOR":2204,"REGCLASS":2205,"REGTYPE":2206,"UUID":2950,"TXID_SNAPSHOT":2970,"PG_LSN":3220,"PG_NDISTINCT":3361,"PG_DEPENDENCIES":3402,"TSVECTOR":3614,"TSQUERY":3615,"GTSVECTOR":3642,"REGCONFIG":3734,"REGDICTIONARY":3769,"JSONB":3802,"REGNAMESPACE":4089,"REGROLE":4096}},"text":{},"binary":{}},"RowCtor":null,"rowAsArray":false} +389ms
  loopback:connector:postgresql SQL: CREATE  INDEX "sound_created_at_idx" ON "public"."sound" ( "created_at" ) +365ms
  loopback:connector:postgresql:data {"command":"CREATE","rowCount":null,"oid":null,"rows":[],"fields":[],"_types":{"_types":{"arrayParser":{},"builtins":{"BOOL":16,"BYTEA":17,"CHAR":18,"INT8":20,"INT2":21,"INT4":23,"REGPROC":24,"TEXT":25,"OID":26,"TID":27,"XID":28,"CID":29,"JSON":114,"XML":142,"PG_NODE_TREE":194,"SMGR":210,"PATH":602,"POLYGON":604,"CIDR":650,"FLOAT4":700,"FLOAT8":701,"ABSTIME":702,"RELTIME":703,"TINTERVAL":704,"CIRCLE":718,"MACADDR8":774,"MONEY":790,"MACADDR":829,"INET":869,"ACLITEM":1033,"BPCHAR":1042,"VARCHAR":1043,"DATE":1082,"TIME":1083,"TIMESTAMP":1114,"TIMESTAMPTZ":1184,"INTERVAL":1186,"TIMETZ":1266,"BIT":1560,"VARBIT":1562,"NUMERIC":1700,"REFCURSOR":1790,"REGPROCEDURE":2202,"REGOPER":2203,"REGOPERATOR":2204,"REGCLASS":2205,"REGTYPE":2206,"UUID":2950,"TXID_SNAPSHOT":2970,"PG_LSN":3220,"PG_NDISTINCT":3361,"PG_DEPENDENCIES":3402,"TSVECTOR":3614,"TSQUERY":3615,"GTSVECTOR":3642,"REGCONFIG":3734,"REGDICTIONARY":3769,"JSONB":3802,"REGNAMESPACE":4089,"REGROLE":4096}},"text":{},"binary":{}},"RowCtor":null,"rowAsArray":false} +3s

Expected Behavior

The created tables should have default values.

Link to reproduction sandbox

Additional information

Related Issues

Ok, I found a fix. I don't think this is documented anywhere, but with postgresql, you have to use dbDefault, not default. So changing my model to the following fixed the issue:

import {Entity, model, property} from '@loopback/repository';

@model({
  settings: {
  }
})
export class Sound extends Entity {

  @property({
    type: 'string',
    id: true,
    required: true,
    length: 17,
    postgresql:
    {
       "dataType":"VARCHAR",
       "dataLength":17,
       "nullable":"N"
    }
  })
  sound_id: string;

  @property({
    type: 'string',
    postgresql:
    {
       "dataType":"VARCHAR",
       "dataLength": 256,
       "nullable":"y",
       dbDefault: "'heelloi'"
    },
  })
  title?: string;

  @property({
    type: 'string',
    postgresql:
    {
       "dataType":"VARCHAR",
       "dataLength": 1024,
       "nullable":"n"
    }
  })
  url: string;

  @property({
    postgresql: {
      dataType: 'TIMESTAMP WITH TIME ZONE',
      dbDefault: 'CURRENT_TIMESTAMP'
    },
    "index": true
  })
  created_at: string;

  constructor(data?: Partial<Sound>) {
    super(data);
  }
}

export interface SoundRelations {
  // describe navigational properties here
}

export type SoundtWithRelations = Sound & SoundRelations;