Skip to content

Commit 5150a7f

Browse files
committed
feat: use correct CREATE and ALTER syntax for PostgreSQL ENUM columns, and provide ENUM labels in grid cells
Refs #1125
1 parent 733b8a5 commit 5150a7f

File tree

4 files changed

+83
-53
lines changed

4 files changed

+83
-53
lines changed

source/dbconnection.pas

Lines changed: 78 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ TDBConnection = class(TComponent)
490490
FMaxRowsPerInsert: Int64;
491491
FCaseSensitivity: Integer;
492492
FSQLFunctions: TSQLFunctionList;
493+
FNamedEnums: TStringList;
493494
procedure SetActive(Value: Boolean); virtual; abstract;
494495
procedure DoBeforeConnect; virtual;
495496
procedure StartSSHTunnel(var FinalHost: String; var FinalPort: Integer);
@@ -624,6 +625,7 @@ TDBConnection = class(TComponent)
624625
function IsHex(Text: String): Boolean;
625626
function Has(Item: TFeatureOrRequirement): Boolean;
626627
property SqlProvider: TSqlProvider read FSqlProvider;
628+
property NamedEnums: TStringList read FNamedEnums;
627629
published
628630
property Active: Boolean read FActive write SetActive default False;
629631
property Database: String read FDatabase write SetDatabase;
@@ -2147,6 +2149,9 @@ constructor TDBConnection.Create(AOwner: TComponent);
21472149
FStringQuoteChar := '''';
21482150
FCollationTable := nil;
21492151
FCharsetTable := nil;
2152+
FQuoteChar := '"';
2153+
FQuoteChars := '"[]';
2154+
FNamedEnums := TStringList.Create;
21502155
end;
21512156

21522157

@@ -2171,8 +2176,6 @@ constructor TAdoDBConnection.Create(AOwner: TComponent);
21712176
i: Integer;
21722177
begin
21732178
inherited;
2174-
FQuoteChar := '"';
2175-
FQuoteChars := '"[]';
21762179
SetLength(FDatatypes, Length(MSSQLDatatypes));
21772180
for i:=0 to High(MSSQLDatatypes) do
21782181
FDatatypes[i] := MSSQLDatatypes[i];
@@ -2186,7 +2189,6 @@ constructor TPgConnection.Create(AOwner: TComponent);
21862189
i: Integer;
21872190
begin
21882191
inherited;
2189-
FQuoteChar := '"';
21902192
FQuoteChars := '"';
21912193
SetLength(FDatatypes, Length(PostGreSQLDatatypes));
21922194
for i:=0 to High(PostGreSQLDatatypes) do
@@ -2203,8 +2205,6 @@ constructor TSQLiteConnection.Create(AOwner: TComponent);
22032205
i: Integer;
22042206
begin
22052207
inherited;
2206-
FQuoteChar := '"';
2207-
FQuoteChars := '"[]';
22082208
SetLength(FDatatypes, Length(SQLiteDatatypes));
22092209
for i:=0 to High(SQLiteDatatypes) do
22102210
FDatatypes[i] := SQLiteDatatypes[i];
@@ -2218,8 +2218,6 @@ constructor TInterbaseConnection.Create(AOwner: TComponent);
22182218
i: Integer;
22192219
begin
22202220
inherited;
2221-
FQuoteChar := '"';
2222-
FQuoteChars := '"[]';
22232221
SetLength(FDatatypes, Length(InterbaseDatatypes));
22242222
for i:=0 to High(InterbaseDatatypes) do
22252223
FDatatypes[i] := InterbaseDatatypes[i];
@@ -2234,6 +2232,7 @@ destructor TDBConnection.Destroy;
22342232
FKeepAliveTimer.Free;
22352233
FFavorites.Free;
22362234
FInformationSchemaObjects.Free;
2235+
FNamedEnums.Free;
22372236
if FOwnsParameters then
22382237
FParameters.Free;
22392238
inherited;
@@ -2309,7 +2308,7 @@ function TDBConnection.GetDatatypeByName(var DataType: String; DeleteFromSource:
23092308
TypesSorted.Free;
23102309
end;
23112310

2312-
rx.Expression := '^('+Types+')\b(\[\])?';
2311+
rx.Expression := '\b('+Types+')\b(\[\])?';
23132312
Match := rx.Exec(DataType);
23142313
// Prefer a later match which is longer than the one found before.
23152314
// See http://www.heidisql.com/forum.php?t=17061
@@ -3389,8 +3388,8 @@ procedure TDBConnection.DoAfterConnect;
33893388
var
33903389
i: Integer;
33913390
TypeOid: String;
3392-
SubTypes: TDBQuery;
3393-
SubTypesList: TStringList;
3391+
AllEnums: TDBQuery;
3392+
AllEnumsList: TStringList;
33943393
SQLFunctionsFileOrder: String;
33953394
MajorMinorVer, MajorVer: String;
33963395
StartupScript: String;
@@ -3416,19 +3415,26 @@ procedure TDBConnection.DoAfterConnect;
34163415
end;
34173416
end
34183417

3419-
else if ExecRegExprI('^[a-z]$', Datatypes[i].NativeTypes) then begin
3418+
else if (Datatypes[i].NativeTypes = 'e') and FSqlProvider.Has(qGetEnumTypes) then begin
34203419
// PG ENUM types populated via 'e'
3421-
if FSqlProvider.Has(qGetSubDataTypes) then begin
3422-
SubTypes := GetResults(FSqlProvider.GetSql(qGetSubDataTypes, [Datatypes[i].NativeTypes]));
3423-
SubTypesList := TStringList.Create;
3424-
while not SubTypes.Eof do begin
3425-
SubTypesList.Add(SubTypes.Col('enum_name'));
3426-
SubTypesList.Add(SubTypes.Col('enum_schema') + '.' + SubTypes.Col('enum_name'));
3427-
SubTypes.Next;
3428-
end;
3429-
SubTypes.Free;
3430-
Datatypes[i].Names := Implode('|', SubTypesList);
3420+
AllEnums := GetResults(FSqlProvider.GetSql(qGetEnumTypes));
3421+
AllEnumsList := TStringList.Create;
3422+
while not AllEnums.Eof do begin
3423+
AllEnumsList.Add(AllEnums.Col('enum_name'));
3424+
AllEnumsList.Add(AllEnums.Col('enum_schema') + '.' + AllEnums.Col('enum_name'));
3425+
FNamedEnums.AddPair(
3426+
AllEnums.Col('enum_name'),
3427+
AllEnums.Col('enum_labels')
3428+
);
3429+
FNamedEnums.AddPair(
3430+
AllEnums.Col('enum_schema') + '.' + AllEnums.Col('enum_name'),
3431+
AllEnums.Col('enum_labels')
3432+
);
3433+
AllEnums.Next;
34313434
end;
3435+
AllEnums.Free;
3436+
Datatypes[i].Names := Implode('|', AllEnumsList);
3437+
AllEnumsList.Free;
34323438
end;
34333439

34343440
end;
@@ -9131,19 +9137,33 @@ function TDBQuery.ValueList(Column: Integer): TStringList;
91319137
i: Integer;
91329138
begin
91339139
Result := TStringList.Create;
9134-
Result.QuoteChar := '''';
9135-
Result.Delimiter := ',';
91369140
ColAttr := ColAttributes(Column);
91379141
if Assigned(ColAttr) then case ColAttr.DataType.Index of
9142+
91389143
dbdtEnum, dbdtSet: begin
9139-
Result.DelimitedText := ColAttr.LengthSet;
9140-
// Take care for escaped ENUM definitions, see issue #799
9144+
// Lool up PostgreSQL enum labels in prefetched list
9145+
i := FConnection.NamedEnums.IndexOfName(ColAttr.LengthSet);
9146+
if i > -1 then begin
9147+
Result.Delimiter := '|';
9148+
Result.DelimitedText := FConnection.NamedEnums.ValueFromIndex[i];
9149+
end
9150+
else begin
9151+
// .. or in MySQL Length/Set
9152+
Result.QuoteChar := '''';
9153+
Result.Delimiter := ',';
9154+
Result.DelimitedText := ColAttr.LengthSet;
9155+
end;
9156+
// In any case, take care for escaped ENUM definitions, see issue #799
91419157
for i:=0 to Result.Count-1 do begin
91429158
Result[i] := FConnection.UnescapeString(Result[i]);
91439159
end;
91449160
end;
9145-
dbdtBool:
9161+
9162+
dbdtBool: begin
9163+
Result.Delimiter := ',';
91469164
Result.DelimitedText := 'true,false';
9165+
end;
9166+
91479167
end;
91489168
end;
91499169

@@ -10754,25 +10774,29 @@ function TTableColumn.SQLCode(OverrideCollation: String=''; Parts: TColumnParts=
1075410774
end;
1075510775

1075610776
if InParts(cpType) then begin
10757-
case FConnection.Parameters.NetTypeGroup of
10758-
ngPgSQL: begin
10759-
if DefaultType = cdtAutoInc then
10760-
Result := Result + 'SERIAL'
10761-
else
10762-
Result := Result + DataType.Name;
10777+
10778+
if FConnection.Parameters.IsAnyPostgreSQL and (DefaultType = cdtAutoInc) then begin
10779+
Result := Result + 'SERIAL';
10780+
end
10781+
else begin
10782+
10783+
if (DataType.Index = dbdtEnum) and (FConnection.NamedEnums.IndexOfName(LengthSet) > -1) then begin
10784+
Result := Result + LengthSet;
10785+
end
10786+
else begin
10787+
Result := Result + DataType.Name;
10788+
if (LengthSet <> '') and DataType.HasLength then
10789+
Result := Result + '(' + LengthSet + ')';
1076310790
end;
10764-
else Result := Result + DataType.Name;
10765-
end;
1076610791

10767-
if (LengthSet <> '') and DataType.HasLength then
10768-
Result := Result + '(' + LengthSet + ')';
10769-
if (DataType.Category in [dtcInteger, dtcReal]) and Unsigned then
10770-
Result := Result + ' UNSIGNED';
10771-
if (DataType.Category in [dtcInteger, dtcReal]) and ZeroFill then
10772-
Result := Result + ' ZEROFILL';
10773-
if Compressed and FConnection.Parameters.IsMariaDB then
10774-
Result := Result + ' /*!100301 COMPRESSED*/';
10775-
Result := Result + ' '; // Add space after each part
10792+
if (DataType.Category in [dtcInteger, dtcReal]) and Unsigned then
10793+
Result := Result + ' UNSIGNED';
10794+
if (DataType.Category in [dtcInteger, dtcReal]) and ZeroFill then
10795+
Result := Result + ' ZEROFILL';
10796+
if Compressed and FConnection.Parameters.IsMariaDB then
10797+
Result := Result + ' /*!100301 COMPRESSED*/';
10798+
Result := Result + ' '; // Add space after each part
10799+
end;
1077610800
end;
1077710801

1077810802
if InParts(cpAllowNull) and (not IsVirtual) and (not FConnection.Parameters.IsAnyMSSQL) then begin
@@ -10883,8 +10907,11 @@ procedure TTableColumn.ParseDatatype(Source: String);
1088310907
LengthSet := '';
1088410908
end else begin
1088510909
LengthSet := '';
10886-
if DataType.Index = dbdtEnum then
10887-
LengthSet := OrgSource;
10910+
if DataType.Index = dbdtEnum then begin
10911+
// Assign PostgreSQL enum type to LengthSet, so we can provide it in table editor
10912+
// Some enum types are wrapped in double quotes
10913+
LengthSet := OrgSource.Trim([FConnection.QuoteChar]);
10914+
end;
1088810915
end;
1088910916
Unsigned := ExecRegExpr('\bunsigned\b', Source.ToLowerInvariant);
1089010917
ZeroFill := ExecRegExpr('\bzerofill\b', Source.ToLowerInvariant);
@@ -10927,8 +10954,12 @@ function TTableColumn.AutoIncName: String;
1092710954
function TTableColumn.FullDataType: String;
1092810955
begin
1092910956
Result := DataType.Name;
10930-
if not LengthSet.IsEmpty then
10931-
Result := Result + '(' + LengthSet + ')';
10957+
if not LengthSet.IsEmpty then begin
10958+
if (DataType.Index = dbdtEnum) and (FConnection.NamedEnums.IndexOfName(LengthSet) > -1) then
10959+
Result := LengthSet
10960+
else
10961+
Result := Result + '(' + LengthSet + ')';
10962+
end;
1093210963
end;
1093310964

1093410965

source/dbstructures.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ interface
5050
qForeignKeyDrop, qGetTableColumns, qGetCollations, qGetCollationsExtended, qGetCharsets,
5151
qGetReverseForeignKeys, qExplain, qSetTimezone,
5252
qShowFunctionStatus, qShowProcedureStatus, qShowTriggers, qShowEvents, qShowCreateTrigger,
53-
qHelpKeyword, qShowWarnings, qGetSubDataTypes);
53+
qHelpKeyword, qShowWarnings, qGetEnumTypes);
5454
TSqlProvider = class
5555
strict protected
5656
FNetType: TNetType;

source/dbstructures.postgresql.pas

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -727,20 +727,20 @@ function TPostgreSQLProvider.GetSql(AId: TQueryId): string;
727727
' WHERE pg_class.relkind=''r'''+
728728
' AND pg_namespace.nspname=:EscapedDatabase'+
729729
' AND pg_class.relname=:EscapedName';
730-
qGetSubDataTypes: Result := IfThen(
730+
qGetEnumTypes: Result := IfThen(
731731
FServerVersion >= 90000,
732732
'SELECT ' +
733733
' n.nspname AS enum_schema, ' +
734734
' t.typname AS enum_name, ' +
735-
' string_agg(e.enumlabel, '','' ORDER BY e.enumsortorder) AS enum_labels ' +
735+
' string_agg(e.enumlabel, ''|'' ORDER BY e.enumsortorder) AS enum_labels ' +
736736
'FROM pg_type AS t ' +
737737
'JOIN pg_enum AS e ' +
738738
' ON t.oid = e.enumtypid ' +
739739
'JOIN pg_namespace AS n ' +
740740
' ON n.oid = t.typnamespace ' +
741-
'WHERE t.typtype = ''%s'' ' +
741+
'WHERE t.typtype = ''e'' ' +
742742
'GROUP BY n.nspname, t.typname ' +
743-
'ORDER BY n.nspname, t.typname',
743+
'ORDER BY UPPER(t.typname)',
744744
'' // ServerVersion < 9
745745
);
746746
else Result := inherited;

source/table_editor.pas

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1866,7 +1866,6 @@ procedure TfrmTableEditor.listColumnsCreateEditor(Sender: TBaseVirtualTree;
18661866
EnumEditor.AllowCustomText := True;
18671867
EnumEditor.ItemMustExist := False;
18681868
EnumEditor.ValueList := Explode('|', Col.DataType.Names);
1869-
EnumEditor.ValueList.Sort;
18701869
EnumEditor.ValueList.Insert(0, '');
18711870
EditLink := EnumEditor;
18721871
end;

0 commit comments

Comments
 (0)