Firebird 3.0

כתבתי והזכרתי כאן המון פעמים את Firebird – הלא הוא מסד נתונים חופשי לגמרי וחוצה פלטפורמות עם המון עצמה וכוח.

למשל, שלומי נוח, איש DBA מומחה MySQL, רשם בתחילת השנה העברית החדשה, כי הוא רוצה מסד נתונים שבו ניתן לקבוע עם כמה מעבדים ניתן לעבוד, ובכן Firebird תומך בזה כבר מספר שנים.

לFirebird יש מספר תחבירי SQL, וחשוב להבין אותם לפני הבנה של התכונות החדשות. סוג התחבירים הם:

  • DSQL
  • ESQL
  • PSQL

DSQL – הם בעצם השאילתות שכותבים ומריצים בצורה דינאמית באמצעות API. המקדם D מייצג את המילה Dynamic.
ESQL – הם בעצם שאילתות עם preprocessor, כאלו שכותבים למשל דרך תוכנה שלנו. ההבדל בינה לבין DSQL היא שבשפה זו משתמשים בפקודה EXEC. הפירוש של המקדם E מייצג את המילה Embedded.
PSQL – השפה שבה כותבים stored procedure וטריגרים. המקדם של P מייצג את המילה Procedural.

בנוסף ישנה שפה בשם DDL – ‏Data Definition Language. זו השפה בה עושים פעולות כדוגמת create table או create database.

לאחר הסבר קצר על התחביר, ניתן להבין פיטצ'רים שונים ולאן הם שייכים.

גרסה 3.0 של Firebird שכרגע בזמן כתיבת הפוסט, עדיין נמצא בשלבי בטא, וב2015 צריכה להשתחרר, מביאה איתה המון חידושים ותוספות מעניינות.
הנה רשימה חלקית בלבד של אותם התכונות החדשות:

SQL – כלל השפות:

  • Merge Syntax
  • פונקציות window
  • תמיכה ב regex בביצוע SUBSTRING
  • נכנס סוג נתונים בשם Boolean
  • יכולת לשמור גרסאות עם RDB$RECORD_VERSION
  • cursor יציב

PSQL:

  • פונקציות SQL
  • פרוצדורות
  • טריגרים, פונקציות ופרוצדורות משפות חיצוניות
  • חבילות
  • חריגות עם פרמטרים
  • SQLSTAT במצב של WHEN
  • שימוש ב continue בתוך לולאות.
  • cursor יכול להחזיר התייחסות כמשתנה מסוג רשומה
  • cursor דו כיווני

DDL‏:

  • תמיכה במצב null של עמודה ו domain
  • היכולת לשנות קידוד ברירת המחדל של מסד הנתונים
  • הוספה של Identity Column – המקביל ל serial בPG ו Auto Increment של SQLITE ו MySQL
  • תחביר עבור RECREATE SEQUENCE/GENERATOR
  • טריגרים עבור DDL
  • תמיכה ב DATABASE LINGER

אבטחה:

  • תמיכה database encryption
  • תמיכה מורחבת יותר בהרשאות Object
  • הרשאות לעבודה עם DDL
  • הרשאות ברמת מסד הנתונים
  • תוספות לעבודה עם Roles

פיקוח:

  • הוספת יכולות נוספות לסטטיסטיקה אודות שאילתות ומסד הנתונים בכלל

אז מה זה כל הסעיפים האלו ?

Merge מאפשר לנו ליצור שאילתא שמאפשרת לנו לאחד שאילתא להוספה ושאילתא לשינוי מידע. כלומר אם המידע קיים כבר, אז הוא יעדכן, ואם לא הוא יוסיף את הרשומה. במקום לבדוק בעצם האם רשומה קיימת ואז להחליט בתוך Stored Procedure למשל, זה כבר חלק מהתחביר.
זה נעשה בצורה כזו עבור טבלה בשם Users:

MERGE INTO Users
   USING User
      ON Users.ID = User.ID

   WHEN MATCHED AND User.Deleted = TRUE THEN
     DELETE

   WHEN MATCHED THEN
      UPDATE SET username=User.username ...

   WHEN NOT MATCHED THEN
      INSERT (username, ...) VALUES (User.username, ...);

ההדגמה מראה איך אפשר לאחד פעולות בשאילתא בודדת לפי תנאים שונים, וזה עוד ברמה של פירוש השאילתא עצמה, מה שמתבצע מהר מאוד ונכון יותר עבורינו, במקום לממש את זה בתוכנה עצמה.

פונקציות Window:
פונקציות חלון מאפשרות לבצע פעולות כדוגמת אגריגציה של מידע, פונקציות דרגות ועוד, על שדות וטבלאות שונות השיכות לשאילתא, בלי ליצור עוד שדות נוספים באיחוד.

SELECT A, B, C,
       SUM(C) OVER() AS SUMA,
       SUM(C) OVER(ORDER BY A, B) AS SUMB,
       SUM(C) OVER(PARTITION BY A) AS SUMOVER1,
       SUM(C) OVER(PARTITON BY A ORDER BY B) AS SUMOVER2;
+-----+-----+-----+--------+--------+------------+------------+
|  A  |  B  |  C  |  SUMA  |  SUMB  |  SUMOVER1  |  SUMOVER2  |
+-----+-----+-----+--------+--------+------------+------------+
|  1  |  1  | 30  |  141   |   30   |     60     |     30     |
+-----+-----+-----+--------+--------+------------+------------+
|  1  |  1  | 20  |  141   |   50   |     60     |     50     |
+-----+-----+-----+--------+--------+------------+------------+
|  1  |  3  | 10  |  141   |   60   |     60     |     60     |
+-----+-----+-----+--------+--------+------------+------------+
|  2  |  1  | 25  |  141   |   85   |     40     |     25     |
+-----+-----+-----+--------+--------+------------+------------+
|  2  |  2  | 15  |  141   |  100   |     40     |     40     |
+-----+-----+-----+--------+--------+------------+------------+
|  3  |  1  | 41  |  141   |  141   |     41     |     41     |
+-----+-----+-----+--------+--------+------------+------------+

Cursor יציב:
פעם היו מספר בעיות:

1. הוספה אין סופית של רשומות:

insert into t
select * from t;

2. מחיקה של יותר שורות מאשר מה שנדרש:

delete from t
  where id in (select first 1 ID from t)

הסיבה לבעיות האלו היו בגלל הצורה ש DML עבד:
פעולות של insert/update/delete סומנו להתבצע לפני כל דבר אחר.
היתרון בזה הוא בכל שהרשומות יציבות מאוד ולא מושפעות בגלל כתיבת DML עצמו.
אבל יש לזה מספר חסרונות:

  • סימון כזה צריך להישמר איפשהו, ורשומות יזכו ליותר מגישה אחת לשם כך
  • שורות שלמות אשר סומנו ישמרו איפשהו ושמירה זו כל הזמן תזכה ל"ביקור"

היתה לבעיה זו דרך מעקף באמצעות שימוש בSORT עם שאילתת PLAN, אשר יצרה cursor בלתי תלוי ויציב יותר.

דרך הפתרון של Firebird 3 היא על ידי יצירה של undo-log בשביל לראות האם רשומה שונתה על ידי ה cursor:

  • אם רשומה קיבלה פעולת inset – התעלם
  • אם רשומה עודכנה או נמחקה – קרא את הגרסה הקודמת

יתרונות:

  • אין צורך יותר ב bookkeeping
  • אין צורך במקום נוסף לשמירה
  • קל יחסית למימוש

החסרונות:

  • הוספה של רשומות יזכו לביקור (למרות ההתעלמות)
  • צריך לקרוא גרסאות ישנות של פעולות של שינוי ומחיקה
  • לא עובד עם SUSPEND הקיים ב PSQL

Global Temporary Table – GTT
אלו טבלאות זמניות הנמצאות בזיכרון במקום בדיסק. זה אומר כי גם אם מסד הנתונים הוא לקריאה בלבד, או שזו טרנזקציה לקריאה בלבד, עדיין ניתן לכתוב לטבלה שכזו.
כלומר זה עובד גם עם GT ON COMMIT PRESERVE ROWS וגם ב COMMIT DELETE ROWS כאשר האחרון עובד גם במצב של טרנזאקציה של קריאה בלבד בתוך מסד נתונים לקריאה בלבד.
זה גם מאפשר לבצע rollback מהיר יותר היות ולא צריך יותר לגבות את הרשומות בקריאה ל rollback.
בנוסף לכך, GC על GTT מתבצע מיידית ולא דורש לחכות לבקשות נוספות שיגרמו לו להתבצע.
בנוסף, כל היכולות האלו נכנסו גם לגרסה 2.5.1 של Firebird ולא ייחודיים יותר רק לגרסה 3.0.

פונקציות:
בFirebird, שפת ה PSQL מאוד חזקה, אבל עד היום אפשרה רק פרוצדורות – כלומר יכולת לבצע פעולות שלמות על מסד הנתונים.
בגרסה 3.0, נכנסו גם פונקציות, אשר מסוגלות רק להחזיר ערכים ולבצע עליהם חישובים, אבל לא להשפיע על טבלאות או רשומות. כלומר רק פעולות חישוב.

הדגמה לפונקציה:

create function inc(x int) returns int
as
begin
  return x + 1;
end;

select inc(5) from RDB$DATABASE; -- 6

חבילות:
חבילה מאפשרת להפריד בין לוגיקה ל API של פונקציה ופרוצדורה.
זה אומר שניתן למשל ליצור פרוצדורה בשם extract_user, אשר מסתמכת על עוד פונקציות ופרוצדורות נוספות, אבל הן לא גלויות ואנחנו נראה ונוכל לגשת רק ל extract_user:

-- package header, declarations only
create or alter package users
as
begin
   procedure extract_user(id int) returns (u int); -- public procedure
end

-- package body - actual implementation
recreate package body users
as
begin
  function valid_user(id int) returns boolean; -- private function
  
   procedure extract_user(id int) returns (u int)
   as 
   begin
   -- ...
   end;

   function valud_user(id int) returns boolean
   as
   begin
    -- ...
   end;
end;

חריגות עם פרמטרים:
עד לגרסה 3.0, היה ניתן "להרים" חריגה פשוטה בלי יותר מידי יכולות.
בגרסה 3.0, נוספה היכולת להוסיף פרמטרים:

create exception ex_with_params 'Error @1 : @2'; -- create exception with two parameters
exception ex_with_params using (1, 'I''m sorry, Dave. I''m afraid I can''t do that.'); -- call the exception with two parameters

שימוש בCursor כמשתנה רשומה:
גרסה 3.0 מאפשרת לנו בעצם להחזיר cursor במצב PSQL:

--- ...
FOR SELECT A, B, C FROM ...
  AS CURSOR C1 -- no INTO clause
DO
BEGIN
   ...
   INSERT INTO ...
   VALUES (C1.A, C1.B, C1.C); -- cursor reference
END

שדה Identity:
בPg, SQLite ו MySQL/MariaDB יש הגדרת שדה שהוא auto increment. שדה שכזה נקרא ב SQL "טהור" שדה identity.
בFirebird, יוצרים עד לגרסה 3 טריגר ו Sequence ואז מחברים בניהם, ובכך יוצרים תמיכה לזה.
לאור ביקוש רב המון שנים, החליטו ליצור תמיכה לסוג ל identity אשר נתמך רק עם numeric ו int על סוגיו:

create table objects (
id integer generated by default as identity primary key,
name varchar(15)
);

insert into objects (name) values (‘Table’);
insert into objects (name) values (‘Book’);
insert into objects (id, name) values (10, ‘Computer’);

select * from objects;
ID           NAME
============ ===============
1             Table
2             Book
10            Computer

טריגר בזמן DDL:
עד גרסה 3.0, היה ניתן ליצור טריגרים רק על מידע. בגרסה החדשה, ניתן ליצור גם טריגרים על אלמנטים שנוצרים ומוגדים כחלק מה DDL.
המידע יושב בטבלה RDB$GET_CONTEXT ומכילה מספר משתנים לקריאה בלבד המוגדרים רק בתוך טריגר השייך ל DDL אשר קיבל את ה namespace של DDL_TRIGGER:

  • DDL_EVENT – סוג האירוע בתוך ה DDL
  • OBJECT_NAME – שם המטאדאטא של אובייקט
  • SQL_TEXT – שאילתת ה SQL כטקסט שצריכה להתבצע

הדגמה לשימוש שכזה:

create exception ex_bad_sp_name 'Name of Procedures must start with ''@1'' : ''@2''';

create trigger trg_sp_create before create procedure
as
declare sp_name varchar(255);
begin
  sp_name = RDB$GET_CONTEXT('DDL_TRIGGER', 'OBJECT_NAME');
  
  if (sp_name not starting 'SP_') then
    exception ex_bad_sp_name using ('SP_', SP_NAME);
end;

תמיכה ב Linger למסד נתונים:
Database Linger זו בעצם יכולת להחזיק בזיכרון מידע מסוים השייך למסד הנתונים גם כאשר החיבור האחרון למסד הנתונים נסגר.
יכולות כזו, מאפשרת בעצם לשמור על ביצועים גבוהים יותר, היות ולא צריך לטעון מחדש מידע אם מתבצע חיבור חדש בזמן ה"שהות" שהוגדר, ובכך פתיחה וסגירה של מסד נתונים כמעט ולא מכיל איזשהו "עונש" על סגירה שלו.

בFb התמיכה בlinger היא בשניות ומתבצעת בצורה כזו:

alter database set linger to 30; -- will set linger interval to 30 seconds

ניתן לבטל את הגדרות ה linger בצורות הבאות:

-- 1.
alter database drop linger;       -- will make engine do not delay closing given database
-- 2.
alter database set linger to 0;   -- another way to clean linger settings

תמיכה מורחבת יותר בהרשאות Object:
נוספה יכולת לשים טאגים ביצירת משתמשים במסד נתונים:

CREATE USER Vlad PASSWORD impaler INACTIVE;
ALTER USER Vlad ACTIVE;
ALTER USER Vlad SET TAGS (id = 1, x = 'abcd');
ALTER USER Vlad SET TAGS (x = 'xyz');
ALTER USER Vlad SET TAGS (DROP x);

המידע נשמר בשני טבלאות מערכת חדשות. ההבדלים מפעם הם אלו:

CREATE TABLE SEC$USERS
(
SEC$USER_NAME    RDB$USER,
SEC$FIRST_NAME   SEC$NAME_PART,
SEC$MIDDLE_NAME  SEC$NAME_PART,
SEC$LAST_NAME    SEC$NAME_PART,
SEC$ACTIVE       RDB$BOOLEAN,
SEC$ADMIN        RDB$BOOLEAN,
SEC$DESCRIPTION  RDB$DESCRIPTION
);
CREATE TABLE SEC$USER_ATTRIBUTES
(
SEC$USER_NAME   RDB$USER,
SEC$KEY         SEC$KEY,
SEC$VALUE       SEC$VALUE
);

כתיבת תגובה

הזינו את פרטיכם בטופס, או לחצו על אחד מהאייקונים כדי להשתמש בחשבון קיים:

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s