PDA

Ver la Versión Completa : Capturar eventos de postgres en delphi


chelard
28-08-2010, 18:49:20
Hola:

Mi pregunta radica en como capturo el evento de inserción de un registro en una tabla, y luego poder saber esto en delphi 7, esto como alternativa, ya que no puedo estar haciendo consultas todo el tiempo a la tabla ya que me imagino que consumiría muchos recursos.

Les comento como va el problema.
El trabajo es para un grifo, en donde hay un programa que almacena en una tabla en postgres 8.4 el total del combustible atendido, por cada surtidor, Ahora lo que tiene que hacer el programa que estoy haciendo es tomar estos registros ingresados y emitir un comprobante, y que lo haga de la forma mas automatica posible.

Por lo cual necesito capturar este evento que se produce en postgres, para realizar la operacion desde delphi.

Estoy utilizando UNIDAC para hacer la conexion con postgres, he visto que hay un componente uniAlerter, sera que esto me pueda servir, He visto en otro Hilo que generan un procedimiento almacenado en firebird, y lo capturan con IBEvents, me imagino que se debe hacer de una forma similar.


Espero alguien pueda darme un camino a seguir.
gracias

Casimiro Notevi
28-08-2010, 19:41:18
En el componente DataSet que estés usando tendrás todos los eventos habituales, onNewRecord, onBeforePost, OnAfterPost, OnBeginUpdate, etc, ...

chelard
28-08-2010, 20:12:24
Hola Casimiro,
Gracias por contestar.

con el evento OnNewRecord del Dataset, es que puedo saber que se inserto un nuevo registro en la tabla?, Es otro programa el que realiza esta insercion, si es asi me puedes dar un pequeño ejemplo.

Casimiro Notevi
28-08-2010, 20:21:11
Mejor explica en detalle todo el proceso que realizas y lo que quieres conseguir.

chelard
28-08-2010, 20:32:00
Hola Casimiro: :)

Tengo una tabla ventas en postgres 8.4 en esta tabla un programa (del cual no tengo codigo fuente), inserta nuevos registros.

Lo que yo quiero es desde mi programa, saber si se inserto un nuevo registro, para inmediatamente recuperar este registro, Lo primero que se me ocurrio era leer periodicamente la tabla, pero esto me consumiria muchos recursos.

Si necesitas mas datos solo hazmelo saber.

Gracias

Casimiro Notevi
28-08-2010, 20:53:30
Pues entonces lo tienes complicado, desconozco si existe alguna utilidad para postgresql que haga lo que necesitas, aunque no me extrañaría, la verdad.

yapt
11-09-2010, 17:08:11
Hola Chelard,

efectivamente tienes que utilizar UniAlerter (yo utilizo PgDAC en lugar de UniDAC pero deberían ser similares).

Para ello, desde la aplicación cliente debes "escuchar" los eventos asincronos de PostgreSQL (te debes suscribir como cliente) y luego tendrás que generar esos eventos (un trigger -after insert row- de base de datos puede servir).

Revisa esta parte del manual de PostgreSQL:
http://www.postgresql.org/docs/8.4/static/sql-notify.html (este es el más completo)
http://www.postgresql.org/docs/8.4/static/sql-listen.html

Jugue con ello hace tiempo, pero no tengo nada en producción, así que seré de poca ayuda a partir de aquí.

Suerte.

chelard
11-09-2010, 17:34:32
Hola Yapt,

Gracias por responder, en general ya tenia la idea lo que quiero realmente es si alguien me puede dar una pauta, un poco mas practico.

en este enlace esta lo que quiero, pero trabaja con firebird,

http://www.delphiaccess.com/forum/index.php?topic=3135.0


alguien que tenga unos conocimientos de triggers en postgres creo que lo podria resolver facilmente, desgraciadamente aun no tengo esa habilidad,

Espero que este enlace ayude a resolver el tema.

Me dice que aun no puedo enviar enlaces, no lei nada al respecto en la guia, en fin puse el enlace en el titulo, espero no cometer ninguna infraccion, solo con el fin de aclarar el post.

gracias

yapt
11-09-2010, 23:41:03
Chelard,

normalmente esto no funciona así. :( Tienes unas directrices generales, al menos intentalo y vuelve con las dudas concretas que puedas tener.

Pero veo que, al menos, eres de los que responden y.... bueno... que aquí va (para referencia futura):

En PostGreSQL (8.4):

/*
Creamos la función que será llamada por el TRIGGER.
*/

CREATE OR REPLACE FUNCTION "f_eventoInsert"()
RETURNS trigger AS
$BODY$BEGIN
NOTIFY pgEventoInsert;
RETURN NEW;
END;$BODY$
LANGUAGE 'plpgsql' VOLATILE COST 100;

/*
Creamos la tabla.
*/
CREATE TABLE "testEventos"
(
id numeric(5,0) NOT NULL,
nombre character varying(10),
CONSTRAINT "pk_testEventos" PRIMARY KEY (id)
)
WITH (OIDS=FALSE);


/*
Creamos el Trigger y lo asociamos a la tabla.
*/
-- Trigger: tg_test_eventoInsert on "testEventos"
-- DROP TRIGGER "tg_test_eventoInsert" ON "testEventos";
CREATE TRIGGER "tg_test_eventoInsert"
AFTER INSERT
ON "testEventos"
FOR EACH ROW
EXECUTE PROCEDURE "f_eventoInsert"();



Y en Delphi. Esto está hecho con los controles de las Devart (PgDAC) que son los que yo tengo licenciados y no con UNIDAC que es el que has comprado tú (tengo la esperanza de que así sea).


Delphi:

{ El formulario.... }

object Form1: TForm1
Left = 382
Top = 212
Caption = 'Form1'
ClientHeight = 255
ClientWidth = 288
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Memo1: TMemo
AlignWithMargins = True
Left = 3
Top = 87
Width = 282
Height = 165
Align = alClient
TabOrder = 0
ExplicitLeft = 40
ExplicitTop = 144
ExplicitWidth = 185
ExplicitHeight = 89
end
object Panel1: TPanel
AlignWithMargins = True
Left = 3
Top = 3
Width = 282
Height = 78
Align = alTop
BevelOuter = bvNone
TabOrder = 1
ExplicitWidth = 178
object Button1: TButton
Left = 8
Top = 8
Width = 75
Height = 25
Caption = 'Conectar'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 89
Top = 16
Width = 75
Height = 25
Caption = 'Suscribir'
TabOrder = 1
OnClick = Button2Click
end
object Button3: TButton
Left = 89
Top = 45
Width = 75
Height = 25
Caption = 'DeSuscribir'
TabOrder = 2
OnClick = Button3Click
end
object Button4: TButton
Left = 8
Top = 53
Width = 75
Height = 25
Caption = 'Desconectar'
TabOrder = 3
OnClick = Button4Click
end
end
object PgConnection1: TPgConnection
Username = 'usuario'
Password = 'clave'
Server = 'localhost'
Connected = True
LoginPrompt = False
Database = 'postgres'
Left = 195
Top = 19
end
object PgAlerter1: TPgAlerter
Events = 'pgEventoInsert'
Connection = PgConnection1
OnEvent = PgAlerter1Event
Left = 203
Top = 67
end
end



{ El Código.... }

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, DBAccess, PgAccess, StdCtrls, ExtCtrls, DAAlerter, PgAlerter;

type
TForm1 = class(TForm)
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
PgConnection1: TPgConnection;
PgAlerter1: TPgAlerter;
procedure Button1Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure PgAlerter1Event(Sender: TObject; const EventName: string;
PID: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
PgConnection1.Connect;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
PgAlerter1.Active := true;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
PgAlerter1.Active := false;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
PgConnection1.Disconnect;
end;

procedure TForm1.PgAlerter1Event(Sender: TObject; const EventName: string;
PID: Integer);
begin
Memo1.Lines.Add('['+TimeToStr(now)+']'+'El proceso: ' + IntToStr(PID) + ' ha lanzado el evento: ' + EventName);
end;

end.

Cuando lo conviertas a UNIDAC, deja aquí los cambios para que sirvan para futuros visitantes.

Gracias.

chelard
12-09-2010, 02:09:49
Hola Yapt:

Muchisimas gracias por responder, y practicamente lo hiciste todo, mucho mas de lo que esperaba,

Si lo intente, desgraciadamente el primer paso era el trigger y ahi es donde me quede,

Manos a la obra para pasarlo a Unidac y en seguida posteo los cambios,

Vamos te mereces un millon de gracias, me has ahorrado mucho trabajo.:D
Volvere pronto para poner los cambios.

chelard
12-09-2010, 03:40:38
Hola Yapt:
Muchas gracias nuevamente funciona perfecto.

Bueno y como me lo pediste dejo aca el codigo para que funcione con los componentes Unidac.

Primeramente el trigger y la BD, igual como tu lo hiciste, el formulario quedo asi


object Form1: TForm1
Left = 438
Top = 103
Width = 349
Height = 289
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Memo1: TMemo
Left = 8
Top = 80
Width = 329
Height = 169
Lines.Strings = (
'Memo1')
TabOrder = 0
end
object btn1: TButton
Left = 121
Top = 45
Width = 75
Height = 25
Caption = 'DeSuscribir'
TabOrder = 1
OnClick = btn1Click
end
object btn2: TButton
Left = 121
Top = 16
Width = 75
Height = 25
Caption = 'Suscribir'
TabOrder = 2
OnClick = btn2Click
end
object btn3: TButton
Left = 8
Top = 53
Width = 75
Height = 25
Caption = 'Desconectar'
TabOrder = 3
OnClick = btn3Click
end
object btn4: TButton
Left = 8
Top = 8
Width = 75
Height = 25
Caption = 'Conectar'
TabOrder = 4
OnClick = btn4Click
end
object unipg1: TPostgreSQLUniProvider
Left = 208
Top = 8
end
object con1: TUniConnection
ProviderName = 'PostgreSQL'
Port = 5432
Database = 'BasedeDatos'
Username = 'Usuario'
Password = 'clave'
Server = 'localhost'
Connected = True
LoginPrompt = False
Left = 208
Top = 32
end
object ale1: TUniAlerter
Connection = con1
Events = 'pgEventoInsert'
OnEvent = ale1Event
Left = 240
Top = 8
end
end



... Y el codigo quedo de esta manera,


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, DB, DBAccess, Uni, UniProvider,
PostgreSQLUniProvider, DAAlerter, UniAlerter;

type
TForm1 = class(TForm)
unipg1: TPostgreSQLUniProvider;
con1: TUniConnection;
ale1: TUniAlerter;
Memo1: TMemo;
btn1: TButton;
btn2: TButton;
btn3: TButton;
btn4: TButton;
procedure btn4Click(Sender: TObject);
procedure btn2Click(Sender: TObject);
procedure btn1Click(Sender: TObject);
procedure btn3Click(Sender: TObject);
procedure ale1Event(Sender: TDAAlerter; const EventName,
Message: String);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btn4Click(Sender: TObject);
begin
con1.Connect;
end;

procedure TForm1.btn2Click(Sender: TObject);
begin
ale1.Active := True;


end;

procedure TForm1.btn1Click(Sender: TObject);
begin
ale1.Active := False;
end;

procedure TForm1.btn3Click(Sender: TObject);
begin
con1.Disconnect;
end;

procedure TForm1.ale1Event(Sender: TDAAlerter; const EventName,
Message: String);
begin
Memo1.Lines.Add('['+TimeToStr(now)+']'+'El proceso: ha lanzado el evento: ' + EventName);
end;

end.

yapt
12-09-2010, 10:38:38
Me alegro que haya sido fácil trasladar el código a UNIDAC.

Suerte.