如何从 ActiveRecord 访问隐藏列(在 Informix 中)

发布于 2024-08-19 04:08:28 字数 464 浏览 3 评论 0原文

许多数据库都有隐藏的主键列。 MySQL 将其实现为 _rowid。在 MySQL 的例子中,它实际上是一个指向先前定义的主键列的指针。然而,在其他数据库(在我的例子中,Informix)中,该列独立于故意定义的主键。我正在编码的数据库主要是根据 Informix 隐藏列 ROWID 而不是定义主键的实践来设计的。我正在使用 Informix JDBC 驱动程序从 JRuby on Rails 连接到 Informix。一切都很好,只是我无法找出使隐藏的 rowid 列显示为 ActiveRecord 属性的方法。所有其他字段都在那里,只是没有 rowid。如果我使用 Whatever.find_by_sql("SELECT rowid,* FROMwhatevertable") 查询模型,它会返回一个“Whatever”对象数组,但 rowid 列不存在。

我研究了调整 JDBC 驱动程序、ActiveRecord 或数据库;一切都没有取得成果。

任何指导将不胜感激。

Many databases have a hidden primary key column. MySQL implements this as _rowid. In MySQL's case it is really a pointer to a previously defined primary key column. However in other databases (in my case, Informix), this column is independent of a deliberately defined primary key. The database which I'm coding for was designed mostly under the practice of depending on the Informix hidden column, ROWID instead of defining a primary key. I'm connecting to Informix from JRuby on Rails using the Informix JDBC driver. All is good except that I cannot figure out a way to make the hidden rowid column show up as an ActiveRecord property. All of the other fields are there, just not rowid. If I query a model using Whatever.find_by_sql("SELECT rowid,* FROM whatevertable") it returns an array of "Whatever" objects but the rowid column is not there.

I've investigated tweaking the JDBC driver, ActiveRecord or the database; nothing has borne fruit.

Any guidance would be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

十年不长 2024-08-26 04:08:28

首先,请注意,如果您的表是碎片化的,则它将不会有 ROWID - 除非您使用WITH ROWIDS 子句创建它,在这种情况下,ROWID 将成为索引物理列而不是虚拟列。

其次,为什么不直接使用表的声明主键,而不是在幕后进行深入研究 - 特别是当幕后深入研究版本不起作用时。

我不清楚为什么您使用的显式符号没有按照您的要求进行;这可能是 JDBC 驱动程序把它的鼻子伸到了它不该伸手的地方。您在结果集中返回了多少列?如果它不是您选择的数字(表中的列数加一),那么有些事情就很可疑。

如果我需要查看 JDBC 驱动程序发送到服务器的内容,我将启用 SQLI 调试跟踪 - 无论是在客户端,还是在服务器端(如果客户端不配合)。在普通(基于 C 的)API 中,启用 SQLI 调试只需设置一个环境变量:

SQLIDEBUG=2:sqli

这将在运行 C 的进程的当前目录中创建一个名称以“sqli_”开头的文件。 API(ESQL/C、ODBC 等)。我认为 JDBC 驱动程序应该使用相同的机制。

如果 SQLIDEBUG 不能与 JDBC 一起使用,那么您的工作就会困难得多 - 您需要在服务器端启用 SQLI 调试。

假设您捕获了 SQLI(SQL 接口)输出,则可以使用“sqliprint”打印它。您将查找发送到 IDS 的 SQL。如果它不包含 ROWID,那么您可以相当确定 JDBC 驱动程序正在与您玩愚蠢的游戏。但是,尚不清楚您将采取什么措施来解决这个问题。也许可以尝试使用表别名(例如“t”)并且:

SELECT t.ROWID, t.* FROM WhateverTable t WHERE ...

如果事实证明 JDBC 正在调整 ROWID,我们也可以尝试使用别名:(

SELECT ROWID AS pk_column, t.* FROM WhateverTable AS t WHERE ...

表名称后面的 AS 是可选的。)

如果您发现任何问题,请随时通知我们有趣或有用。


数据库已安装在多个位置,并且已编译应用程序(其源不可用),如果数据库架构发生更改,这些应用程序将会中断,因此我无法添加合法的主键。这些应用程序依赖 rowid 作为主键。

好的;如果您的任何客户拥有足够大的数据量,碎片化可能是有益的,那么您应该在下一次主要升级时审查这一设计决策。请记住,碎片表没有虚拟 ROWID 列;您可以通过在 CREATE TABLE 或 ALTER FRAGMENT 语句中使用WITH ROWIDS 子句来使用物理 ROWID 列创建它们。

使用 log4jdbc,我能够确认发送到 Informix 的查询确实包含对 rowid 的请求。返回值,并且 JDBC 驱动程序对 ResultSet 中的每一列进行强制转换;我可以看到它正在转换 rowid 列(ResultSet.getLong())。但是,返回的 ActiveRecord 对象不包含 rowid 值。我相信这是因为当 JDBC 驱动程序被要求提供表的架构(用于建立 ActiveRecord 类的可用属性)时,不会返回 rowid。任何意见都将受到赞赏...

在获取信息方面做得很好。顺便说一句,我对 JDBC 驱动程序可能执行的操作的了解开始耗尽 - 这可能需要向 IBM/Informix 技术支持寻求帮助。我的脑海中浮现出各种问题 - 我什至不确定问题是否出在 JDBC 驱动程序、整体 JDBC 设计中,或者是位于 JDBC 之上的某些东西;我无论如何都不是这个领域的专家。 (我可以拼写 Java - C;对吗?)

您是否尝试过将 ROWID 放入语句的最后一列中?

SELECT *, ROWID FROM WhereEver

那么别名呢?

SELECT *, ROWID AS T_RowID FROM WhereEver

如果 JDBC 驱动程序看穿了这两点,那么它就工作得太辛苦了,给每个人带来了伤害。事实上,我什至不确定我是否知道它如何与视图一起使用。

我有一个元素表:

CREATE TABLE elements
(
    atomic_number   INTEGER NOT NULL UNIQUE CONSTRAINT c1_elements
                    CHECK (atomic_number > 0 AND atomic_number < 120),
    symbol          CHAR(3) NOT NULL UNIQUE CONSTRAINT c2_elements,
    name            CHAR(20) NOT NULL UNIQUE CONSTRAINT c3_elements,
    atomic_weight   DECIMAL(8,4) NOT NULL,
    stable          CHAR(1) DEFAULT 'Y' NOT NULL
                    CHECK (stable IN ('Y', 'N'))
);

INSERT INTO elements VALUES(  1, 'H',   'Hydrogen',        1.0079, 'Y');

我能够在该表上创建一个视图(包括 ROWID),然后对其进行查询:

CREATE VIEW x_elements(atomic_number, symbol, name, x_rowid)
    AS SELECT atomic_number, symbol, name, ROWID AS x_rowid
         FROM elements;

SELECT * FROM x_elements WHERE x_rowid <  512;
SELECT * FROM x_elements WHERE x_rowid >= 512;

这两个查询生成了不相交的数据集。您也许可以欺骗您的系统来利用此漏洞。如有必要,您可以重命名基表(例如,“WhateverTable”变为“Base_Whatever”),然后创建视图“WhateverTable”以从 Base_Whatever 中选择数据以及 ROWID。我还没有用 JDBC 程序正式尝试过这一点,但它“应该”工作(但由于原始查询也“应该”工作,我不确定我会在多大程度上依赖“应该工作”断言)。

First, note that if your table is fragmented, it won't have a ROWID - unless you created it using the WITH ROWIDS clause, in which case the ROWID becomes an indexed physical column instead of a virtual column.

Second, why don't you just use the declared primary key for the table instead of delving behind the scenes - especially when the delving behind the scenes version is not working.

I'm not clear why the explicit notation you are using is not doing as you request; that could be the JDBC driver poking its nose in where its nose does not belong. How many columns do you get back in the result set? If it isn't the number you select (the number of columns in the table plus one), then something is very fishy.

If I needed to see what the JDBC driver is sending to the server, I would enable SQLI debug tracing - either at the client side or, if the client side does not cooperate, on the server side. In normal (C-based) APIs, enabling SQLI debug is a matter of setting an environment variable:

SQLIDEBUG=2:sqli

This will create a file with a name starting 'sqli_' in the current directory of the process running the C API (ESQL/C, ODBC, etc). I assume that the same mechanism should work with the JDBC driver.

If SQLIDEBUG does not work with JDBC, then you have a much harder job - you need to enable SQLI debug on the server side.

Assuming you trap the SQLI (SQL Interface) output, you can then print it using 'sqliprint'. You'd look for the SQL sent to IDS. If it does not contain the ROWID, then you can be fairly sure the JDBC driver is playing silly games with you. However, it is not clear what you'd do to work around that. Maybe try a table alias (such as 't') and:

SELECT t.ROWID, t.* FROM WhateverTable t WHERE ...

If it turns out that JDBC is tweaking ROWID, we could try aliassing that too:

SELECT ROWID AS pk_column, t.* FROM WhateverTable AS t WHERE ...

(The AS is optional after the table name.)

Please keep us posted if you discover anything interesting or useful.


The database is already installed in multiple locations and has compiled applications (for which source is unavailable) which would break if the database schema changes, so I cannot add legit primary keys. These applications rely on rowid as a primary key.

OK; that is a design decision that should be reviewed at your next main upgrade if any of your customers have large enough volumes of data that fragmentation might be beneficial. Remember, fragmented tables do not have a virtual ROWID column; you can create them with a physical ROWID column by using the WITH ROWIDS clause in the CREATE TABLE or ALTER FRAGMENT statements.

Using log4jdbc I was able to confirm that the query being sent to Informix does include the request for rowid. The value is returned and the JDBC driver casts each column in the ResultSet; I can see that it is casting the rowid column (ResultSet.getLong()). The ActiveRecord object returned does not, however, include the rowid value. I believe that this is because when the JDBC driver is asked for the schema of the table, which is used to establish the available properties of the ActiveRecord class, the rowid is not returned. Any input is appreciated...

Well done on getting the information. Offhand, my knowledge of what the JDBC driver might be doing starts to run out - this might be something to take to IBM/Informix Technical Support. Various questions arise in my mind - and I'm not even sure whether the problem is in the JDBC driver, the JDBC design overall, or something sitting above JDBC; I am not by any stretch of my imagination an expert in the area. (I can spell Java - C; is that right?)

Have you tried fiddling the ROWID into the last column in the statement?

SELECT *, ROWID FROM WhereEver

What about aliassing that?

SELECT *, ROWID AS T_RowID FROM WhereEver

If the JDBC driver sees through both those, then it is working far too hard and doing everyone a disservice. Indeed, I'm not even sure I know how it works with views.

I have a table of elements:

CREATE TABLE elements
(
    atomic_number   INTEGER NOT NULL UNIQUE CONSTRAINT c1_elements
                    CHECK (atomic_number > 0 AND atomic_number < 120),
    symbol          CHAR(3) NOT NULL UNIQUE CONSTRAINT c2_elements,
    name            CHAR(20) NOT NULL UNIQUE CONSTRAINT c3_elements,
    atomic_weight   DECIMAL(8,4) NOT NULL,
    stable          CHAR(1) DEFAULT 'Y' NOT NULL
                    CHECK (stable IN ('Y', 'N'))
);

INSERT INTO elements VALUES(  1, 'H',   'Hydrogen',        1.0079, 'Y');

I was able to create a view on that table including the ROWID and then query it:

CREATE VIEW x_elements(atomic_number, symbol, name, x_rowid)
    AS SELECT atomic_number, symbol, name, ROWID AS x_rowid
         FROM elements;

SELECT * FROM x_elements WHERE x_rowid <  512;
SELECT * FROM x_elements WHERE x_rowid >= 512;

The two queries produced disjoint sets of data. You may be able to finagle your system to exploit this. If necessary, you'd rename the base tables (e.g. 'WhateverTable' becomes 'Base_Whatever') and then create the view 'WhateverTable' to select the data from Base_Whatever, along with the ROWID. I haven't formally tried this with a JDBC program, but it 'ought' to work (but since the original query also 'ought' to work, I am not sure how much reliance I would place on the 'ought to work' assertion).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文