unit uDAPostgresInterfaces; {----------------------------------------------------------------------------} { Data Abstract Library - Core Library } { } { compiler: Delphi 6 and up, Kylix 3 and up } { platform: Win32, Linux } { } { (c)opyright RemObjects Software. all rights reserved. } { } { Using this code requires a valid license of the Data Abstract } { which can be obtained at http://www.remobjects.com. } {----------------------------------------------------------------------------} {$I DataAbstract.inc} interface uses SysUtils, uDAInterfaces, uDAEngine, uROClasses; type { IDAPostgresConnection For identification purposes Implemented by all Postgres connections } IDAPostgresConnection = interface(IDAConnection) ['{D8EADB7E-7AA0-48FF-9E7D-34853F999BFC}'] end; TDAPostgresDriver = class(TDAEDriver) protected function GetDefaultConnectionType(const AuxDriver: string): string; override; safecall; public end; TDAEPostgresConnection = class(TDAEConnection, IDAPostgresConnection, IDACanQueryDatabaseNames, IDAUseGenerators) protected function DoGetLastAutoInc(const GeneratorName: string): integer; override; procedure DoGetTableFields(const aTableName: string; out Fields: TDAFieldCollection); override; // IDACanQueryDatabaseNames function GetDatabaseNames: IROStrings; // IDAUseGenerators function GetNextAutoinc(const GeneratorName: string): integer; safecall; end; function Postgres_GetDatabaseNames(aConnection: TDAEConnection): IROStrings; function Postgres_DoGetLastAutoInc(const GeneratorName: string; Query: IDADataset): integer; function Postgres_GetNextAutoInc(const GeneratorName: string; Query: IDADataset): integer; procedure Postgres_DoGetTableFields(const aTableName: string; Query: IDADataset; out Fields: TDAFieldCollection); function PostgresDataTypeToDA(aDataType: string; Unicode: Boolean): TDADataType; const PostgreSQL_DriverType = 'PostgreSQL'; implementation const Postgres_MasterDatabase = 'template1'; Postgres_GetDatabaseNames_SQL = 'SELECT datname FROM pg_database ORDER BY datname'; function PostgresDataTypeToDA(aDataType: string; Unicode: Boolean): TDADataType; begin aDataType := LowerCase(aDataType); if pos(' ', aDataType) <> 0 then Delete(aDataType, Pos(' ', aDataType), MaxInt); if (aDAtaType = 'varchar') or (aDataType = 'charactervarying') or (aDataType = 'character') or (aDataType = 'char') then begin if Unicode then Result := datWideString else Result := datString; end else if aDataType = 'text' then begin if Unicode then Result := datWideMemo else Result := datMemo; end else if (aDataType = 'blob') or (aDataType = 'binary') or (aDAtaType = 'varbinary') then Result := datBlob else if (aDataType = 'date') or (aDataType = 'time') or (aDataType = 'timetz') or (aDataType = 'datetime') or (aDataType = 'timestamp') or (aDataType = 'timestamptz') or (aDataType = 'year') then result := datDateTime else if (aDataType = 'single') or (aDataType = 'real') or (aDataType = 'float4') then Result := datSingleFloat else if (aDataType = 'double') or (aDataType = 'float') or (aDataType = 'doubleprecision') or (aDataType = 'float8') then Result := datFloat else if (aDataType = 'bit') or (aDataType = 'boolean') or (aDataType = 'bool') then Result := datBoolean else if (aDataType = 'bigint') or (aDataType = 'int8') then result := datLargeInt else if (aDataType = 'decimal') or (aDataType = 'numeric') then result := datDecimal else if (aDataType = 'money') then result := datCurrency else if (aDataType = 'serial') or (aDataType = 'serial4') then result := datAutoInc else if (aDataType = 'bigserial') or (aDataType = 'serial8') then result := datLargeAutoInc else if (aDataType = 'smallint') or (aDataType = 'int2') then result := datSmallInt else if (aDataType = 'shortint') or (aDataType = 'int1') then result := datShortInt else if (aDataType = 'enum') or (aDataType = 'int4') or (adatatype = 'int') or (adatatype = 'integer') or (aDataType = 'tinyint') then result := datInteger else result := datUnknown; end; function Postgres_GetDatabaseNames(aConnection: TDAEConnection): IROStrings; begin Result := Engine_GetDatabaseNames(aConnection, Postgres_MasterDatabase, Postgres_GetDatabaseNames_SQL); end; function Postgres_DoGetLastAutoInc(const GeneratorName: string; Query: IDADataset): integer; begin try Query.SQL := 'SELECT currval(''' + GeneratorName + ''')'; Query.Open; result := Query.Fields[0].Value; finally Query := nil; end; end; function Postgres_GetNextAutoInc(const GeneratorName: string; Query: IDADataset): integer; begin try Query.SQL := 'SELECT nextval(''' + GeneratorName + '''text)'; Query.Open; result := Query.Fields[0].Value; finally Query := nil; end; end; procedure Postgres_DoGetTableFields(const aTableName: string; Query: IDADataset; out Fields: TDAFieldCollection); const main_sql = 'SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, CHARACTER_MAXIMUM_LENGTH, CHARACTER_SET_NAME ' + 'FROM INFORMATION_SCHEMA.COLUMNS ' + 'WHERE TABLE_NAME=''{tbl}'' and TABLE_SCHEMA=''{schem} ''' + 'ORDER BY ORDINAL_POSITION'; pk_sql = 'SELECT COLUMN_NAME ' + 'FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE ' + 'WHERE TABLE_NAME=''{tbl}'' and TABLE_SCHEMA=''{schem}'' AND CONSTRAINT_NAME like ''%_pkey'''; var fld : TDAField; lSchema, lTable : string; p1 : integer; s : string; begin Fields := TDAFieldCollection.Create(nil); try Query.SQL := 'SELECT * FROM ' + aTableName + ' WHERE 1=0'; Query.Open; Fields.Assign(Query.Fields); Query.Close; Query.SQL := main_sql; lTable := aTableName; if pos('.', lTable) = 0 then begin lSchema := 'public' end else begin lSchema := copy(lTable, 1, pos('.', lTable) - 1); Delete(lTable, 1, pos('.', lTable)); end; Query.SQL := StringReplace(Query.SQL, '{tbl}', lTable, []); Query.SQL := StringReplace(Query.SQL, '{schem}', lSchema, []); Query.Open; while not Query.Eof do begin fld := Fields.FieldByName(Query.Fields[0].AsString); fld.Required := Query.Fields[2].AsString <> 'YES'; fld.DefaultValue := Query.Fields[3].AsString; fld.Size := Query.Fields[4].AsInteger; fld.DataType := PostgresDataTypeToDA(Query.Fields[1].asString, sametext(Query.Fields[5].AsString, 'utf8') or sametext(Query.Fields[5].AsString, 'utf16')); if fld.DefaultValue <> '' then begin if pos('nextval(', fld.DefaultValue) = 1 then begin case fld.DataType of datInteger: fld.DataType := datAutoInc; datLargeInt: fld.DataType := datLargeAutoInc; else fld.DefaultValue := ''; end; if fld.DefaultValue <> '' then begin s := fld.DefaultValue; p1 := pos('''', s); Delete(s, 1, p1); p1 := pos('''', s); fld.GeneratorName := Copy(s, 1, p1 - 1); fld.DefaultValue := ''; end; end else if not TestDefaultValue(fld.DefaultValue, fld.DataType) then begin fld.DefaultValue := ''; end; end; Query.Next; end; Query.Close; Query.SQL := pk_sql; Query.SQL := StringReplace(Query.SQL, '{tbl}', lTable, []); Query.SQL := StringReplace(Query.SQL, '{schem}', lSchema, []); Query.Open; while not Query.Eof do begin fld := Fields.FindField(Query.Fields[0].AsString); if fld <> nil then fld.InPrimaryKey := true; Query.Next; end; finally Query := nil; end; end; { TDAEPostgresConnection } function TDAEPostgresConnection.DoGetLastAutoInc( const GeneratorName: string): integer; begin Result := Postgres_DoGetLastAutoInc(GeneratorName, GetDatasetClass.Create(Self)); end; procedure TDAEPostgresConnection.DoGetTableFields(const aTableName: string; out Fields: TDAFieldCollection); begin Postgres_DoGetTableFields(aTableName, GetDatasetClass.Create(Self), Fields); end; function TDAEPostgresConnection.GetDatabaseNames: IROStrings; begin Result := Postgres_GetDatabaseNames(Self); end; function TDAEPostgresConnection.GetNextAutoinc( const GeneratorName: string): integer; begin Result := Postgres_GetNextAutoInc(GeneratorName, GetDatasetClass.Create(Self)); end; { TDAPostgresDriver } function TDAPostgresDriver.GetDefaultConnectionType( const AuxDriver: string): string; begin Result:= PostgreSQL_DriverType; end; end.