Pregunta

I am migrating our Delphi XE2 application from MSSQL (using ADO components), to PostgreSQL, using UniDAC.

In database, there are some serial type fields (autoincrements). When I append record, I not put any data to this autoincrement field. Formely, with MSSQL/ADO it works automatically, but now I have an exception.

The code:

aqrMsgs.Append;
aqrMsgsUser_From.AsInteger := UserId;
aqrMsgsUser_To.AsString := UserIds[I];
aqrMsgsSubject.AsString := Trim (edtSubject.Text);
aqrMsgsContents.AsString := mmoContents.Text;
aqrMsgsIsDone.AsBoolean := False;
aqrMsgs.Post;

And the exception is:

Exception

Field 'id' is TIntegerField, not TAutoIncrementField.

By the way, if I using DBGrid edit ability (exactly, I using ExpressQuantumGrid), to append records to another table with the same structure, everything working OK.

How possible to solve it? Thanks.

¿Fue útil?

Solución

1) When you create a field with the serial type, PostgreSQL server automatically creates a sequence, and values from this sequence will be used as default for this field. If the serial field is not set when inserting a new record, the server takes a value from the sequence for it. But if you set a value for the serial field, the server will insert this value. Since the sequence doesn't know anything about the value you have inserted to the serial field, then on further inserting records (when using the sequence), the "duplicate key value" error can occur, if the serial field is created with unique constraint. You won't encounter this issue if you don't manually set values for this field.

2) UniDAC can automatically fill in a field using a sequence. For this, you should set the TUniQuery.KeyFields and TUniQuery.SpecificOptions.Values['KeySequence'] properties as follows:

  UniQuery1.KeyFields := 'id'; 
  UniQuery1.SpecificOptions.Values['KeySequence'] := 'test1_id_seq';

In addition, using the TUniQuery.SpecificOptions.Values['SequenceMode'] property, you can specify when exactly UniDAC will fill in the field using the sequence: on calling Append/Insert or Post.

You can find the detailed information about the mentioned properties here: http://www.devart.com/unidac/docs/pgsqlprov_article.htm

Otros consejos

Most SQL compliant way would be not to enlist this field in SQL query at all and let it left NULL and then fill it in on the server via SQL Before Insert trigger from null to a unique value.

This can be done if UniDAC has insert query customization like TUpdateSQL was.

Manually specifying INSERT query (or INSERT-RETURNING if you would need the ID later for any reasons: http://en.wikipedia.org/wiki/SQL_INSERT ) is the most controllable, efficient and flexible way to insert the data.


However if you do Append and does not want to write SQL queries for doing it, then you can read the ID value from server before doing post.

1) declare cross-transactional ID source: SQL SEQUENCE

2) query nextval() before doing post and put it into ID field (you can make it in TDataSet.BeforePost event handler)


You can also read UniDAC documentation and see few sequence-related properties in TUniTable that automate this process for you. KeySequence and SequenceMode at http://www.devart.com/unidac/docs/pgsqlprov_article.htm#tuniquery_tunitable_tunistoredproc

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top