Вести с полей
Решил сам для себя изложить суть текущей проблемы 🙂
С начала недели на отстое стоит незакоммиченный код 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
Семен Семеныч …
А ты про десять два с половиной млн символов думал.
В общем, все ясно.
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 и вуаля
Думай ещё. Ты можешь 🙂