Ik probeer het voorbeeld van dit artikel op Linux Journal na meer dan 10 jaar uit met één van de volgende versies van Gambas (3.x) en een andere databank die meer verschilt dan MySQL-MariaDB onderling: PostgreSQL.
Hier de beschrijving die gaat tot het ophalen en weergeven van gegevens (dus niet heel artikel uitgewerkt).
Vertrek van een Linux desktop systeem, met Gambas en PostgreSQL geïnstalleerd.
Het concept van het artikel is een soort summiere bug-tracker te maken.
Gambas IssueTracker
Start Gambas 3.9.x, new project, Database application.
– Je krijgt een lege FMain bij de “sources”.
Maak een knop om de applicatie te sluiten: kies onderaan rechts de “ok” knop uit de “Form” tab iconen, plak hem ergens op het scherm
– op het scherm staat een knop met “ok” erop.
Wijzig de tekst op de knop:
In de tab “Properties” rechts zie je een invulbaar veld naast “Text”; zet daar “Sluiten”.
– De knop op het scherm krijgt aangepaste tekst.
Zet helemaal bovenaan bij Name in plaats van Button1: btQuit
Wijzig de code van de knop:
Dubbelklik op de knop en je komt in de tekstbewerker. Tik tussen de begin en eindcode van de druk-op-de-knop procedure (die hij voor je klaarzet): Me.Close()
Samen ziet het er zo uit:
Public Sub btQuit_Click Me.Close() End
Terwijl je op de run knop in de knoppenbalk van Gambas3 drukt (of F5 op je toetsenbord), weet je dat je een leeg venster op het scherm gaat zien met één knop: “Sluiten”. Je kan je niet bedwingen om op die knop te klikken.
Pauze om na te denken
Het oorspronkelijke artikel vermeldt volgende noden:
- nieuw probleem melden
- bijhouden wie het probleem meldde
- probleem als opdracht toewijzen aan programmeur
- de toestand van het probleem beheren
- bijhouden wanneer het probleem werd gemeld
- bijhouden in welke programmaversie het probleem is opgelost
- overzicht krijgen van nieuwe problemen, problemen waar aan gewerkt wordt, en opgeloste problemen
- …
Meer gedetailleerd hebben we nodig:
- identificatie van de melder
- identificatie van de oplosser
- beschrijving van het probleem
Nog meer gedetailleerd:
- gegevens van de melder: id, name
- gegevens van de oplosser: id, name
- details van het probleem: id, beschrijving, datum in, door wie aangebracht en opgelost, applicatie waarover ze gaat, versienummer van vaststelling probleem en oplossing
Databank
Om databank-layout uit te proberen is phpPgAdmin leuk, maar nadien weet je waarschijnlijk niet meer wat je juist gedaan hebt.
Het is handig om de databank aan te maken vanuit een bestand, zodat je met dat bestand gegarandeerd dezelfde databank kan maken.
(of je draait het om; maak in phpPgAdmin, en exporteer de vorm naar een bestand)
In MySQL/MariaDB was het zoiets:
create database issuetracker; create table issuetracker.issue ( id int auto_increment primary key, description longtext, application_id int, releaseversion float, raisedby_id int, solvedby_id int, status_id int, createdat datetime, priority int, status int);
In postgres doe ik het half op de commandolijn, de rest in phpPgAdmin:
su postgres postgres@linuxbox:/home/cybr/bin> createdb issuetracker Password: postgres@linuxbox:/home/cybr/bin> psql Password: psql (9.4.9) Type "help" for help. postgres=# grant all privileges on database issuetracker to cybr; GRANT
Voor de database begint het zo:
CREATE TABLE issue ( id integer NOT NULL,
maar ik zie dat in postgreSQL de id keys gemaakt worden met SERIAL als veldtype, dan gebeurt de rest vanzelf.
Samengevat:
CREATE TABLE issue ( id SERIAL PRIMARY KEY, details text, application_id integer, release numeric, raisedby_id integer, coder_id integer, status_id integer, created timestamp without time zone, priority integer, status integer ); CREATE TABLE coder ( id SERIAL PRIMARY KEY, surname character varying(50), firstname character varying(50), username character varying(50) ); CREATE TABLE application ( id SERIAL PRIMARY KEY, name character varying(50) ); CREATE TABLE status ( id SERIAL PRIMARY KEY, title character varying(50) );
Ps: upd 07-2017: “PRIMARY KEY” toegevoegd; ik had ze eerst met alleen SERIAL staan:
CREATE TABLE status ( id SERIAL, title character varying(50) );
(Zie uitgebreide dump onderaan* )
Je moet een gebruiker aanmaken die toegang heeft tot de database, of misschien heb je dat voordien gedaan.
Dan een beetje data invoeren:
- een gebruiker (kies zelf, hier “cybr” enz)
- status: new, worked on, rejected, completed
INSERT INTO status VALUES (1, 'new');
INSERT INTO status VALUES (2, 'worked on');
INSERT INTO status VALUES (3, 'rejected');
INSERT INTO status VALUES (4, 'completed');
Gambas 3
Nieuw project, venster FMain.
Menu Project, Components: gb.db aanvinken.
(ps: er is een component specifiek voor MySQL en PostgreSQL, moeten die keuze gemaakt worden of is het optioneel?)
Linkse kolom, boven Sources rechtsklik, Modules, “MData”.
‘ Gambas module file
Daarin komt:
Private myDb As New Connection Public tmpRec As Result Public Function connectdb() As Boolean With myDb .Type = "postgresql" .Host = "localhost" .Login = "cybr" .Password = "_____" .Name = "issuetracker" End With Try myDb.Open If Error Message("Cannot open issuetracker database; " & Error.Text) Return False Endif Return True End Public Function Exec(sSql As String) As Result Return myDb.Exec(sSql) End
In het FMain venster (dubbelklik om terug in de code te komen):
Voer deze code in om beginnend de databankverbinding uit te proberen. Druk F5 om het programma te laten lopen.
Public Sub btQuit_Click() Me.Close() End Public Sub Form_Open() Dim myResult As Result If MData.connectdb() Try myResult = MData.Exec("SELECT id FROM coder WHERE coder.username='" & System.User.Name & "'") If myResult.Available Message.Info("connected as " & System.User.Name) Else Message.Error("could not connect as " & System.User.Name) Endif Endif End
Als het goed gaat krijg je een venster met de melding dat je aangemeld bent als (gebruikernaam. Zoniet krijg je een foutmelding, of crasht/hangt je programma, en moet je op zoek naar de fout.
Daarna maken we (in FMain.class) de code om twee comboboxen op FMain te vullen.
Vervang in FMain Message.Info("connected as...
door
loadCombos()
en voeg die procedure toe in FMain:
Private Sub loadCombos() Dim s As String s = "SELECT i.id FROM issue i, coder c " s &= "WHERE i.coder_id=c.id AND c.username='" & System.User.Name & "'" MData.loadCombo(cmbxBugid, s) MData.loadCombo(cmbxStatus, "SELECT title FROM status") End
En zoals uit deze code blijkt, de loadCombo code in MData:
Public Sub loadCombo(cmbx As ComboBox, sSql As String) cmbx.Clear tmpRec = myDb.Exec(sSql) For Each tmpRec cmbx.Add(tmpRec[0]) Next End
Als alles goed ik draait het project nu en vult het de twee comboboxen met de geselecteerde inhoud uit de betreffende tabellen.
*) Dump uit postgreSQL / phpPgAdmin:
-- -- PostgreSQL database dump -- SET statement_timeout = 0; SET lock_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SET check_function_bodies = false; SET client_min_messages = warning; SET search_path = public, pg_catalog; SET default_tablespace = ''; SET default_with_oids = false; -- -- Name: application; Type: TABLE; Schema: public; Owner: cybr; Tablespace: -- CREATE TABLE application ( id integer NOT NULL, name character varying(50) ); ALTER TABLE application OWNER TO cybr; -- -- Name: application_id_seq; Type: SEQUENCE; Schema: public; Owner: cybr -- CREATE SEQUENCE application_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE application_id_seq OWNER TO cybr; -- -- Name: application_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: cybr -- ALTER SEQUENCE application_id_seq OWNED BY application.id; -- -- Name: coder; Type: TABLE; Schema: public; Owner: cybr; Tablespace: -- CREATE TABLE coder ( id integer NOT NULL, surname character varying(50), firstname character varying(50), username character varying(50) ); ALTER TABLE coder OWNER TO cybr; -- -- Name: coder_id_seq; Type: SEQUENCE; Schema: public; Owner: cybr -- CREATE SEQUENCE coder_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE coder_id_seq OWNER TO cybr; -- -- Name: coder_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: cybr -- ALTER SEQUENCE coder_id_seq OWNED BY coder.id; -- -- Name: issue; Type: TABLE; Schema: public; Owner: cybr; Tablespace: -- CREATE TABLE issue ( id integer NOT NULL, details text, application_id integer, release numeric, raisedby_id integer, coder_id integer, status_id integer, created timestamp without time zone, priority integer, status integer ); ALTER TABLE issue OWNER TO cybr; -- -- Name: TABLE issue; Type: COMMENT; Schema: public; Owner: cybr -- COMMENT ON TABLE issue IS 'issues themselves'; -- -- Name: issue_id_seq; Type: SEQUENCE; Schema: public; Owner: cybr -- CREATE SEQUENCE issue_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE issue_id_seq OWNER TO cybr; -- -- Name: issue_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: cybr -- ALTER SEQUENCE issue_id_seq OWNED BY issue.id; -- -- Name: issue_status_id_seq; Type: SEQUENCE; Schema: public; Owner: cybr -- CREATE SEQUENCE issue_status_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE issue_status_id_seq OWNER TO cybr; -- -- Name: issue_status_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: cybr -- ALTER SEQUENCE issue_status_id_seq OWNED BY issue.status_id; -- -- Name: status; Type: TABLE; Schema: public; Owner: cybr; Tablespace: -- CREATE TABLE status ( id integer NOT NULL, title character varying(50) ); ALTER TABLE status OWNER TO cybr; -- -- Name: status_id_seq; Type: SEQUENCE; Schema: public; Owner: cybr -- CREATE SEQUENCE status_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; ALTER TABLE status_id_seq OWNER TO cybr; -- -- Name: status_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: cybr -- ALTER SEQUENCE status_id_seq OWNED BY status.id; -- -- Name: id; Type: DEFAULT; Schema: public; Owner: cybr -- ALTER TABLE ONLY application ALTER COLUMN id SET DEFAULT nextval('application_id_seq'::regclass); -- -- Name: id; Type: DEFAULT; Schema: public; Owner: cybr -- ALTER TABLE ONLY coder ALTER COLUMN id SET DEFAULT nextval('coder_id_seq'::regclass); -- -- Name: status_id; Type: DEFAULT; Schema: public; Owner: cybr -- ALTER TABLE ONLY issue ALTER COLUMN status_id SET DEFAULT nextval('issue_status_id_seq'::regclass); -- -- Name: id; Type: DEFAULT; Schema: public; Owner: cybr -- ALTER TABLE ONLY issue ALTER COLUMN id SET DEFAULT nextval('issue_id_seq'::regclass); -- -- Name: id; Type: DEFAULT; Schema: public; Owner: cybr -- ALTER TABLE ONLY status ALTER COLUMN id SET DEFAULT nextval('status_id_seq'::regclass); -- -- Name: application_pkey; Type: CONSTRAINT; Schema: public; Owner: cybr; Tablespace: -- ALTER TABLE ONLY application ADD CONSTRAINT application_pkey PRIMARY KEY (id); -- -- Name: coder_pkey; Type: CONSTRAINT; Schema: public; Owner: cybr; Tablespace: -- ALTER TABLE ONLY coder ADD CONSTRAINT coder_pkey PRIMARY KEY (id); -- -- Name: status_pkey; Type: CONSTRAINT; Schema: public; Owner: cybr; Tablespace: -- ALTER TABLE ONLY status ADD CONSTRAINT status_pkey PRIMARY KEY (id); -- -- Name: public; Type: ACL; Schema: -; Owner: postgres -- REVOKE ALL ON SCHEMA public FROM PUBLIC; REVOKE ALL ON SCHEMA public FROM postgres; GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO PUBLIC; -- -- PostgreSQL database dump complete --