Rel Doc

15.3.2016 18:41

Библиотека com/teacode/rel

Методы библиотеки разбиваются на две группы

  • методы описания схемы
  • методы работы с базами

Описания схемы – абстрактные, не привязанные к конкретной базе. Одно описание может работать с несколькими базами, одна база может описываться несколькими эквивалентными схемами.

Можно строить схемы для существующих баз, а затем работать через них.

Действующие примеры работы с базами: http://alpaca.teacode.com/doc/520

Методы декларативного описания баз данных

Схема

Схема – пространство имен, в котором определяются основные объекты базы – таблицы, индексы, последовательности и т.д.

Можно как работать в рамках схемы, заданной в СУБД по умолчанию, так и определить схему явно:

def schema(name:String!) // создание объекта схемы

def defaultSchema // объект дефолтной схемы (схемы, заданной по умолчанию)

Таблица

Определение таблицы в схеме по умолчанию:

def table(name:String!): Table!
Определение таблицы в фиксированной схеме:
fix myschema = rel/schema('MY_SCHEMA)
fix mytable = myschema.table('MY_TABLE)
Объект таблицы описывается в рамках схемы myschema.

Поля (колонки)

Поля типов данных.

Поле целых чисел:

def Table intColumn(name:String!)

Опции поля целых чисел:

def IntColumn bigInt         // опция переключения на тип BIGINT
def IntColumn tinyInt        // опция переключения на TINYINT, пока не работает
def IntColumn smallInt       // опция переключения на SMALLINT, пока не работает

Поле вещественных чисел:

def Table realColumn(name:String!)

Опции поля вещественных чисел:

def RealColumn decimal(precision:Int!, scale:Int?)  // тип DECIMAL(precision, scale)
def RealColumn decimal(precision:Int!)              // тип DECIMAL(precision)

Поле строковых значений:

def Table stringColumn(name:String!)
Опции поля строковых значений:

def StringColumn char(length:Int!) // тип CHAR
def StringColumn max(length:Int!)  // максимальная длина – для всех типов
def StringColumn clob              // тип CLOB
def StringColumn ignoreCase        // тип VARCHAR_IGNORECASE

Поля с автоматическим заданием значений

IdentityColumn:

def Table identityColumn(name:String!) // реализация IDENTITY, первичный ключ
Опции identityColumn:

def _ start(ss:Int!)        // первое значение, по умолчанию = 1
def _ step(st:Int!)         // шаг, по умолчанию = 1

AutoIncrementColumn:

def Table autoIncrementColumn(name:String!) // реализация AUTO_INCREMENT

Опции AutoIncrementColumn:

def _ start(ss:Int!)       // первое значение, по умолчанию = 1
def _ step(st:Int!)        // шаг, по умолчанию = 1

SequenceColumn:

def Table sequenceColumn(name:String!, seq:Sequence!) 
Поле, управляемое глобальной последовательностью seq. Позволяет организовать сквозную нумерацию записей нескольких таблиц.

Поле-внешний ключ

def Table refColumn( name:String!, column: Column!) // 

У значений данного поля тот же тип, что и у поля column.

Общие опции полей

def _ default(val: Const!) // задание значения по умолчанию (вместо `NULL`)
def _ notNull  // запрещены NULL вместо значений
def _ unique   // все значения должны быть различными
def _ comment(text:String!)  // комментарий к полю

Пример формирования таблицы:

  fix coffees = rel/table('COFFEES)
  
  fix id = coffees.identityColumn('ID).start(5).step(10)
  fix name = coffees.stringColumn('COF_NAME).char(15).unique.default("Arabica")
  fix price = coffees.realColumn('PRICE).decimal(4,2).notNull
  fix amount = coffees.intColumn('AMOUNT).bigInt

Получение полей таблицы

def Table getCols: Column* // table columns

Первичные и внешние ключи таблицы

Первичный ключ таблицы определяется с помощью

def Table setPrimary(cols: Column*)
В cols задаются поля, которые в совокупности определяют первичный ключ. Если первичный ключ определяется полем типа identityColumn, то в использовании setPrimary нет необходимости.

Метод для формирования внешнего ключа таблицы:

def Table setReference(cols: Column*, refcols:Column*)
Здесь cols – поля, формирующие внешний ключ, refcols – поля, на которые ссылаются поля внешнего ключа. Поля cols должны принадлежать таблице из контекста метода.

В отличие от setPrimary, метод setReference может быть задействован в таблице несколько раз. Это делается в случае, когда таблица содержит более одного внешнего ключа.

Если внешний ключ содержит только одно поле, то вместо общего ограничения setReference можно использовать специальное поле refColumn.

NULL

NULL представляется методом rel/null. Метод

def _ isNull: Unit?
проверяет, является ли объект нулом или нет.

Глобальные последовательности

Описание последовательности

def sequence(name: String!) // определение последовательности в дефолтной схеме
def Schema sequence(name: String!)  // определение в фиксированной схеме

Опции последовательностей:

def Sequence start(ss:Int!)  // стартовое значение последовательности
def Sequence step(st:Int!)   // шаг последовательности (может быть отрицательным)

Методы формирования запросов к БД

from:

def from(tables: Table+)
def from(t1:Table!, t2:Table!)
def from(t1:Table!, t2:Table!, t3:Table!)
def from(t1:Table!, t2:Table!, t3:Table!, t4:Table!) 
select:
struct SelectTable = Column | Table | Arith

def Query select(rt: SelectTable*)
def Query select(rt0: SelectTable!, rt1: SelectTable!)
...
def Query select(rt0: SelectTable!, ..., rt9: SelectTable!)

where:

def Query where(cond: Condition!)
Реализованы бинарные отношения (пока нужно вставлять в обратные кавычки).
`==`, `<`, `>`, `and`, `or`. 

Оператор сортировки:

def Query `^`(params: (Column | Desc)+)
...
def Query `^`(param: (Column | Desc)!, ..., param4: (Column | Desc)?)

def desc(column: Column!):Desc! // упорядочить значение поля по убыванию

Оператор join

def Query join(table: Table!, condition: Condition!)

def Query innerJoin(table: Table!, condition: Condition!)

def Query leftJoin(table: Table!, condition: Condition!)

def Query rightJoin(table: Table!, condition: Condition!)

Пример:

    rel/from(books). 
      join(authors, authorID `==` authorID). 
      where((published `>` 1820)).
      select(title, name, surname) 

Псевдонимы (alias)

Алиасы в Rel определяются с помощью метода a/1:

def Table a(als: (String | Int)!)

def Column a(als: (String | Int)!)

Здесь als – задаваемый псевдоним таблицы или поля. В качестве алиасов могут браться как строки, так и целые числа. Для вариантов a(0),.., a(9) имеются сокращения a0, .., a9. Алиасы в Rel используются в постфиксном виде. Пример:

fix getspouses = rel/from(persons.a1, persons.a2).
                 where(pid.a1 `==` spouse.a2). 
                 select(name.a1, name.a2)

Группировка записей и методы–агрегаторы

Метод

def Query group(gr: Column+) 
группирует записи по значениям полей gr (может быть одно или более полей). Агрегаторы выполняют групповые операции взятия минимального и максимального значений, вычисления среднего, и общего количества строк в группе:

struct Arith = Avg | Max | Min | Sum | Count

def Query max(cl: NColumn!): Max!

def Query min(cl: NColumn!): Min!

def Query avg(cl: NColumn!): Avg!

def Query sum(cl: NColumn!): Sum!

def Query count(cl: Column!): Count!
Пример:

rel/from(stats).
  group(stats_id).
  select(max(temp_f), min(temp_f), avg(rain_i), stats_id)

Вставка строк в таблицу

def Table row(columns: ManualColumn*, values: Const*):Insert!
def Table row(values: Const*):Insert!
В одноместном row происходит вставка значений для всех полей, управляемых "вручную". Операция row является абстрактной, и не привязана к СУБД. Например:

persons.row(("John", null))
Чтобы осуществить эту операцию в рамках конкретной базы данных, используется метод insert.

Если необходимо, чтобы вставка возвращала первичный ключ вставляемой записи, используется опция key:

Удаление и изменение значений

Данные методы описывают операции удаления и изменения значений в таблицах.

Метод удаления строк (записей) таблиц:

def delete(table: Table!):Query!

Пример:

  fix shipdel = rel/delete(shippers). where(shipperID `==` 2)

Методы, организующие изменения значений полей:

def update(table: Table!)

def Query set(%:Query assign)

def Query `=`(col:Column!, val:Const!)

Для применения этих описаний в конкретных СУБД используйте метод run в рамках транзакций.

Индексирование

Индексы (наряду со схемами, таблицами, последовательностями и строками) являются базовыми объектами модели Rel. Объект индекса создается с помощью метода

def Table indexx(name:String!, columns:Column+)

Здесь name – имя индекса, columns – индексируемые столбцы. Если столбцов несколько, то все они должны принадлежать одной таблице. Для индекса доступны следующие опции:

def Indexx hash // хэш-индекс (для таблиц в памяти, для остальных игнорируется)

def Indexx unique // запрещаются повторения значений

Пример создания объекта индекса:

fix idx1 = table1.indexx('IDX_1, (col1, col2)). unique

Работа в базе

Все методы, представленные выше, обеспечивают абстрактное описание структуры базы данных, и не привязаны к конкретной СУБД. Для применения этих описаний к конкретным базам данных используются следующие методы.

Создание / открытие базы

def db(base:Base!)
Здесь base – хендлер реляционной БД. Примеры (СУБД H2):
use com/teacode/rel
use libretto/h2
 
def main = {
  fix tempbase = rel/db(h2/temp)
  fix permbase = rel/db( h2/dir(io/p("bases/forms1")) )
  ...
}

Методы создания объектов по их описаниям

def Schema create     // создание схемы в БД
def Table create      // создание таблицы в БД
def Sequence create   // создание глобальной последовательности в БД
def Indexx create     // создание индекса в БД

Методы удаления объектов

def Schema drop     // удаление схемы из БД
def Table drop      // удаление таблицы из БД
def Sequence drop   // удаление глобальной последовательности из БД
def Indexx drop     // удаление индекса из БД

Метод вставки табличных строк в БД

def Insert insert
Методы создания и вставки выполняются в контексте транзакции СУБД:

use com/teacode/rel
use libretto/h2
use libretto/io
  
def main = {
  fix persons = rel/table("PERSONS")
  fix name = persons.stringColumn('NAME)
  
  fix insJohn = persons.row(("John"))
 
  fix permbase = rel/db( h2/dir(io/p("bases/forms1")) )
 
  permbase.w: {
    persons.create
 
    fix johnPrimaryKey = insJohn.key.insert
  }
}
Опция key позволяет получить и присвоить переменной johnPrimaryKey первичный ключ только что вставленной строки.

Метод out

Метод out запускает запросы в базе данных – в рамках транзакции. Пример:

fix q = rel/from(climate).
          group(quarter).
          select(city, quarter, count(quarter), min(temp), max(temp))

fix result = q.out

Методы работы с результатом:

Результат выполнения запроса (метода out) представляется в виде структур Result и Row:

struct Const = Real | String | Int | Null | Boolean

struct Result(columns: (Column|Arith)*, rows: Row*)
struct Row(vals: Const*)

Поле columns содержит объекты полей или арифметических операций. Поле rows – последовательность объектов Row, каждый из которых представляет строку ответа – последовательность объектов структуры Const.

Методы получения значений:

// взятие значения у поля номер col строки номер row
def Result get(row:Int!, col:Int!) 

// взятие в первой строке поля с номером col
def Result get(col:Int!) 

// взятие целочисленного значения или пустоты (для NULL) 
// в поле номер col строки номер row
def Result intNull(row:Int!, col:Int!) 

// взятие целочисленного значения или пустоты (для NULL) 
// в поле номер col первой строки                                
def Result intNull(col:Int!) 

// взятие целочисленного значения или пустоты (для null) 
// в поле номер col данной строки
def Row intNull(col:Int!) 

// получение целых чисел без null:
def Result int(row:Int!, col:Int!):Int!
def Result int(col:Int!):Int!
def Row int(col:Int!):Int!

Аналогично для строк и вещественных чисел:

def Result stringNull(row:Int!, col:Int!):String?
def Result stringNull(col:Int!):String?
def Row stringNull(col:Int!):String?

def Result string(row:Int!, col:Int!):String!
def Result string(col:Int!):String!
def Row string(col:Int!):String!

def Result realNull(row:Int!, col:Int!):Real?
def Result realNull(col:Int!):Real?
def Row realNull(col:Int!):Real?

def Result real(row:Int!, col:Int!):Real!
def Result real(col:Int!):Real!
def Row real(col:Int!):Real!

Взятие всех значений столбца (соответствующего поля из каждой строки)

def Result getCol(col:Int!):Const*

и всех полей строки:

def Result getRow(row:Int!):Const*

Проверка результата запроса на пустоту:

def Result empty
def Result nonEmpty

Метод run

Метод run исполняет запросы на удаление и изменение значений в конкретных базах данных.

Пример:

fix shipupdate = 
  rel/update(shippers).
    where(shipperID `==` 1). 
    set: {
      shipperName `=` "Hehe Packing"
      phone `=` "223-322"
    }
...
permbase.w: {
  shipupdate.run
}

Работающие примеры

Смотри http://alpaca.teacode.com/doc/520