Вести с полей

Решил сам для себя изложить суть текущей проблемы 🙂

С начала недели на отстое стоит незакоммиченный код c поддержкой IDENTITY-колонок FB3.

Задача простая — для всех колонок результирующего множества, напрямую отображаемых на таблицы базы данных, нужно выполнить запрос:

select <индекс колонки> from rdb$relation_fields where rdb$relation_name=’<имя таблицы>‘ and rdb$field_name=’<имя колонки>‘ and rdb$identity_type is NOT NULL

Казалось бы, что тут может быть сложного?

0. Сразу выругаюсь на то, что сервер не завозит эти сведения в описаниях колонок через XSQLVAR.

1. Если тип колонки не пригоден для IDENTITY (BLOB, FLOAT, VARCHAR, дата-время) все и так ясно. Запрос выполнять не нужно.

2. Если запрос будет напрямую включать имя таблицы/колонки (то есть он будет без параметров), то перед строкой с именем нужно явно указать кодовую страницу текстовых данных. Для FB3 это _unicode_fss. А для FB4, это уже будет _utf8. Сложно. С параметрами проще — сервер сообщает для них кодовую страницу и провайдер к ней приводит. Это первая проблема.

Вторая проблема этого пункта — специальные символы в названиях объектов.

— Нужно экранировать одиночную кавычку.
— Нужно что-то делать с символом ‘\0’.

Казалось бы, как можно создать таблицу с нулевым символом в названии?

Сделать нельзя. Но можно попытаться. Например так:

execute block
as
begin
 execute statement 'create table "TBL00-1-'|| x'00' ||'-ABC-2" ( ID INTEGER,ID2 INTEGER);';
end

Сервер этот запрос выполняет без ошибок. Но имя будет обрезано на нуле. Что, кстати, говорит о его внутренностях.

Возвращаясь к нашим баранам — нулевой символ нужно выявлять и преобразовывать в x’00’.

В случае параметров ничего делать не надо.

Вообщем — нужно использовать параметры. Слышишь, Дмитрий (это я себе) — переделывай на параметры.

3. Не очень рационально тестировать каждую колонку отдельным запросом. Будет гораздо веселее тестировать их пачками. То есть вышеуказанный запрос объединяется с ему подобными через UNION ALL и вуаля.

Главное что — нужно помнить про 10MB-ый лимит на длину запроса … Поскольку у нас данные могут быть представлены в unicode_fss/utf8 (3 или 4 байта на символ), то 10MB нужно поделить на 4. Получаем максимум ~2.5MB символов в запросе.

Так. Кстати. Если с запрос будет с параметрами, то можно нарваться на ВОТ ЭТО.

И вообще, давай-ка сначала проверим саму возможность выполнения запроса с кучей UNION. На кошках.

option explicit

dim cn

print "connect to database  ..."

set cn=createobject("ADODB.Connection")

cn.Provider="LCPI.IBProvider.5"

cn.Properties("location")="inet4://localhost/d:\database\ram\ibp_test_fb30_d3.gdb"
cn.Properties("user id")="GAMER"
cn.Properties("password")="vermut"
cn.Properties("ctype")="win1251"
cn.Properties("dbclient_type")="fb.direct"
cn.Properties("auto_commit")=true
cn.Properties("provider_error_rules")=0

call cn.Open()

print "generate sql ..."

dim sql
dim i

sql=sql&"select 1 from rdb$relation_fields where rdb$relation_name='TEST_COLUMNS' and rdb$field_name='COL_I2__AI' and rdb$identity_type is NOT NULL"

for i=0 to 10
 sql=sql&vbCr&"union all"&vbCr&sql
next 'i

print "sql length: "&len(sql)

print "execute sql ..."

dim rs

set rs=cn.Execute(sql)

print "fetch result set ..."

dim nRes
nRecs=0

while(not rs.eof)
 nRecs=nRecs+1
 
 call rs.movenext()
wend

print "total record count: "&cstr(nRecs)

private sub print(t)
 wscript.echo t
end sub

d:\Users\Dima\Work\TestCode\ActiveX\IBP>cscript too_many_union.vbs
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

connect to database …
generate sql …
sql length: 305141
execute sql …
d:\Users\Dima\Work\TestCode\ActiveX\IBP\too_many_union.vbs(38, 1) Firebird: Dynamic SQL Error
Too many Contexts of Relation/Procedure/Views. Maximum allowed is 256

Семен Семеныч …

А ты про десять два с половиной млн символов думал.

В общем, все ясно.

One Comment

Vlad  on 13 февраля, 2020

> для всех колонок результирующего множества, напрямую отображаемых на таблицы базы данных, нужно выполнить запрос

Зачем ? Один запрос на таблицу — не получается ?

select , rdb$field_name
from rdb$relation_fields
where rdb$relation_name=’‘
and rdb$identity_type is NOT NULL

Подсказать, как это можно выполнить одним запросом для нескольких таблиц ? Без UNION, если что 🙂

> Сразу выругаюсь на то, что сервер не завозит эти сведения в описаниях колонок через XSQLVAR

А ты поднимал этот вопрос тогда, когда identity появились ?
Насчёт XSQLVAR не буду ничего обещать, но в isc_dsql_info и в IMessageMetadata это можно добавить, по идее.

> Не очень рационально тестировать каждую колонку отдельным запросом

Не может быть ! Правда ? 🙂

> То есть вышеуказанный запрос объединяется с ему подобными через UNION ALL и вуаля

Думай ещё. Ты можешь 🙂

Leave a Comment