Attribute ident can't be retarget when the attribute is part of composite attribute

For some needs I have some alias ident which is moved from one attribute to another one.

Original schema is:

(def post-schema [
                   {
                       :db/ident :Post/title.version.1
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                       :db/fulltext true
                       :IModel/type "db"
                   }
                    {
                       :db/ident :Post/title.version.2
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                       :db/fulltext true
                       :IModel/type "db"
                   }
                   {
                       :db/ident :Post/category
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                       :IModel/type "db"
                   }
                ])
(d/transact connInternal post-schema)

And alias is set firstly for :Post/title.version.1

 (d/transact
      connInternal 
[[:db/add :Post/title.version.1 :db/ident :Post/title]] )

Now, I need to use :Post/title for :Post/title.version.2, it is done very easily by executing the next statements:

 ; Retracting of alias from first version and setting for second one

 (d/transact
      connInternal 
[[:db/add :Post/title :db/ident :Post/title.version.1] ] )

 (d/transact
      connInternal 
[[:db/add :Post/title.version.2 :db/ident :Post/title]] )

So all works fine until :Post/title.version.1 is part of composite attribute, the above statements do not work if the schema is the next:

(def post-schema [
                   {
                       :db/ident :Post/title.version.1
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                       :db/fulltext true
                       :IModel/type "db"
                   }
                   {
                       :db/ident :Post/title.version.2
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                       :IModel/type "db"
                   }
                  {
                       :db/ident :Post/category
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                       :IModel/type "db"
                   }
                 {
                       :db/ident :Post/title.category
                       :db/valueType :db.type/tuple
                       :db/cardinality :db.cardinality/one
                       :db/tupleAttrs [:Post/title.version.1 :Post/category]
                       :IModel/type "db"
                   }
                ])
(d/transact connInternal post-schema)

In such case

 (d/transact
      connInternal 
[[:db/add :Post/title :db/ident :Post/title.version.1] ] )

is executed successfully, even the history shows, that alias ident has been retracted:

But once the next statement is executed:

 (d/transact
      connInternal 
[[:db/add :Post/title.version.2 :db/ident :Post/title]] )

Then the next error happens:

{:cognitect.anomalies/category :cognitect.anomalies/incorrect, :cognitect.anomalies/message "Ident :Post/title cannot be used for entity :Post/title.version.2, already used for :Post/title.version.1", :db/error :db.error/cannot-retarget-ident}

The error states that :Post/title is hold by :Post/title.version.1, but the history confirms that :Post/title is not linked to :Post/title.version.1

Datomic on-prem 0.9.6045

2 Likes

Guys any thoughts on the issue?
@marshall are you aware whether it is expected behaviour?

I would suggest agains changing schema attributes ever.

I think the (schema) idents are cached locally in the local connection, and somehow also resolved locally. So if you do your changes and then reconnects, does it work then?

I would recommend against anyway.

Unfortunately neither transactor restart and/or creating of new connection do not help. The same issue still happens.

Yeah, I’m understanding that such approach is not recommended but on other hand datomic allows such operation. Let me explain why I need alias idents:
There is some kind of master schema, let’s say represented as json, this schema is managed by the customer, and the customer might need to change value type of some field, since datomic does not support such kind of operation because of its history features then a new attribute is created to represent field from the customer’s schema, also there is some functionality which determines attribute ident(e.g. to execute query, transact data) based on name of field. So using of alias idents is ideal way for me since I do not need to think which exact attribute represents field from master schema now since once each time new attribute is created to reprsent field I’m setting alias ident which actually is just name of field. So everything works cool while attribute is not part of tuple attribute.

My suggestion would be to put the master schema key names in some attribute you defined on your own, just like you’ve done with :IModel/type above, and use the fact that the schema entities can have whatever data attached to them just like any other entity. These attributes are not magic and would make it easy to change and put extra data on these.

At some point I guess it would be easier (but maybe not that easy) to have just a meta layer of mappings from the master schema, and just some value-type boxes for various data types referenced by mappings. A lot of things becomes more complicated with queries etc, though.

Even more such approach was firstly developed :slight_smile: But then I got an idea why not to use alias idents, did PoC, all was working good, minus extra meta layer, the code became simpler. But I once I started to test case when some fields might be part of composite field(on datomic I used tuple attributes to reflects such composite fields from the master schema) I noticed such unexpected behaviour. I sill believe it is just some small issue on datomic side which can be easily solved

Ok! :slight_smile: Yes, it’s probably a glitch somewhere.

Can anyone from datomic team give a response on this issue?

@nikolayandr sorry for the late reply I was just pointed to this case from another user experiencing a similar issue. Can you clarify if you are altering the ident only? When I look at the transaction it looks like you are also trying to alter fulltext here which is prohibited. However, you do not expressly mention altering fulltext and I am wondering if the example you have given was somehow altered or cutoff. Also, are you actually transacting this schema together as shown in one transaction? What happens if you make your ident alterations and then transact the composite tuple?

Fulltext can never be altered
https://docs.datomic.com/on-prem/schema.html#schema-alteration

Generally altering an ident is covered here:
https://docs.datomic.com/on-prem/schema.html#renaming-an-identity

Thanks,
Jaret

Hi @jaret

Can you clarify if you are altering the ident only

Right, only ident is altered

When I look at the transaction it looks like you are also trying to alter fulltex

No, fulltext is not altered, it was my mistake to specify it in the example, I will provide step by step example below without using fulltext

What happens if you make your ident alterations and then transact the composite tuple

It works in such way but does not suit our case since composite key is created earlier than alias alteration happens

Step by step example, where each step is separate transaction made to the transactor:

1.Create schema:

(def post-schema [
                   {
                       :db/ident :Post/title.version.1
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                   }
                   {
                       :db/ident :Post/title.version.2
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                   }
                  {
                       :db/ident :Post/category
                       :db/valueType :db.type/string
                       :db/cardinality :db.cardinality/one
                   }
                 {
                       :db/ident :Post/title.category
                       :db/valueType :db.type/tuple
                       :db/cardinality :db.cardinality/one
                       :db/tupleAttrs [:Post/title.version.1 :Post/category]
                   }
                ])
(d/transact conn post-schema)

2.Set alias :Post/title on :Post/title.version.1:

 (d/transact
      conn
[[:db/add :Post/title.version.1 :db/ident :Post/title]])

3.The next step is first one to move alias :Post/title from :Post/title.version.1 to :Post/title.version.2. First of all we should set :Post/title.version.1 as it was originally, also in such way we are making :Post/title free to be used for other attributes:

 (d/transact
      conn
[[:db/add :Post/title :db/ident :Post/title.version.1] ] )

4.This step is second one to finish moving of alias :Post/title from :Post/title.version.1 to :Post/title.version.2:

 (d/transact
      conn 
[[:db/add :Post/title.version.2 :db/ident :Post/title]] )

The 4th step fails because database has attribute :Post/title.category which has :Post/title.version.1 as part of that attribute, in the case DB does not have :Post/title.category available in DB then all is OK

HI @jaret. Could you please tell me whether there are any updates on this issue? The issue still remains for the newest datomic version. Thanks