rails-ai-context 5.35.1 → 5.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +375 -1
- data/CONTRIBUTING.md +2 -1
- data/docs/COMPATIBILITY.md +15 -1
- data/docs/SECURITY.md +70 -40
- data/docs/TOOLS.md +23 -10
- data/docs/TROUBLESHOOTING.md +33 -6
- data/lib/rails_ai_context/database_family/mysql_runtime.rb +19 -5
- data/lib/rails_ai_context/database_family/postgresql_plan.rb +35 -16
- data/lib/rails_ai_context/database_family/postgresql_runtime.rb +43 -8
- data/lib/rails_ai_context/database_family/sqlite_runtime.rb +47 -13
- data/lib/rails_ai_context/database_family.rb +36 -10
- data/lib/rails_ai_context/introspector.rb +12 -0
- data/lib/rails_ai_context/introspectors/declared_constant.rb +19 -7
- data/lib/rails_ai_context/introspectors/eager_load.rb +12 -2
- data/lib/rails_ai_context/introspectors/job_introspector.rb +60 -20
- data/lib/rails_ai_context/introspectors/model_introspector.rb +18 -23
- data/lib/rails_ai_context/introspectors/multi_database_introspector.rb +10 -1
- data/lib/rails_ai_context/introspectors/performance_introspector.rb +0 -1
- data/lib/rails_ai_context/introspectors/schema_introspector.rb +12 -1
- data/lib/rails_ai_context/payload.rb +12 -3
- data/lib/rails_ai_context/secret_columns.rb +120 -72
- data/lib/rails_ai_context/serializers/markdown_serializer.rb +1 -1
- data/lib/rails_ai_context/sql_gate.rb +183 -38
- data/lib/rails_ai_context/tools/get_context.rb +4 -4
- data/lib/rails_ai_context/tools/get_controllers.rb +5 -1
- data/lib/rails_ai_context/tools/get_conventions.rb +10 -5
- data/lib/rails_ai_context/tools/get_schema.rb +35 -11
- data/lib/rails_ai_context/tools/onboard.rb +3 -6
- data/lib/rails_ai_context/tools/query.rb +13 -3
- data/lib/rails_ai_context/tools/runtime_info.rb +23 -14
- data/lib/rails_ai_context/tools/safe_call.rb +2 -3
- data/lib/rails_ai_context/tools/session_context.rb +2 -3
- data/lib/rails_ai_context/version.rb +1 -1
- metadata +1 -23
- data/.rspec +0 -3
- data/.rubocop.yml +0 -16
- data/.ruby-version +0 -1
- data/AGENTS.md +0 -15
- data/CLAUDE.md +0 -3
- data/CODE_OF_CONDUCT.md +0 -44
- data/CONTEXT.md +0 -242
- data/Rakefile +0 -80
- data/docs/RELEASING.md +0 -70
- data/docs/_config.yml +0 -15
- data/docs/_includes/head-custom.html +0 -16
- data/docs/adr/0001-own-listener-registration.md +0 -9
- data/docs/adr/0002-static-tier-declared-not-detected.md +0 -5
- data/docs/adr/0003-shared-tool-cache-semantics.md +0 -31
- data/docs/adr/0004-install-e2e-stays-example-rich.md +0 -28
- data/docs/adr/0008-query-runs-only-where-it-can-guard.md +0 -38
- data/docs/adr/0009-dbhub-shapes-in-ruby.md +0 -26
- data/docs/adr/0010-one-config-tool-with-sections.md +0 -32
- data/docs/agents/domain.md +0 -38
- data/docs/agents/issue-tracker.md +0 -45
- data/docs/agents/triage-labels.md +0 -15
- data/server.json +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8ea836548c12d9e6a022ca0e9cb78f471c2144d5b16197ab9493808209c12f74
|
|
4
|
+
data.tar.gz: 1037665358df3ef107d83530dcec2371728b76df61852c51387b2797eb5bac51
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 007d9bbd31542ad5caa6847ce83443fa3d793483cbb93aefcd37dc03c4f4cbf536e51dfd4735ddcf27e0f6dda46b052a95f7f6317d2685f2b2cb8769da349819
|
|
7
|
+
data.tar.gz: a4815b990a54d309fe3b5a4ce97ab8b7fca7a50841a70825d3aa02378915fe77f0f5465259e3484efb2a8268e959f4877bde376a4617ee461e36e94292eb4301
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,380 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.36.0] - 2026-10-14
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- **On PostgreSQL a name on the read-only VOLATILE list vouches for the server's
|
|
13
|
+
own function and an extension's C function alone.** The list trusted a name in
|
|
14
|
+
any schema, so a VOLATILE function the app defined under one of those names
|
|
15
|
+
ran. While the database holds a VOLATILE function of a listed name in another
|
|
16
|
+
language outside `pg_catalog`, every call of the name is now refused, and the
|
|
17
|
+
refusal says so. 5.35.2 and earlier versions had the gap.
|
|
18
|
+
- **On PostgreSQL 18 a set operation over a secret column is refused, as it is
|
|
19
|
+
on older servers.** PostgreSQL 18 plans a set operation over the tables
|
|
20
|
+
themselves, and the plan check that refuses a secret column passed through a
|
|
21
|
+
set operation did not read that shape. A set operation now counts every column
|
|
22
|
+
its inputs hand up as a use of that column. 5.35.2 and earlier versions had
|
|
23
|
+
the gap on PostgreSQL 18.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **On SQLite a call of `changes()`, `total_changes()` or
|
|
28
|
+
`last_insert_rowid()` is refused also where the app has a table of the same
|
|
29
|
+
name.** The counter answers for the connection that runs the statement,
|
|
30
|
+
which is `rails_query`'s own. 5.35.1 and 5.35.2 ran such a call when a table
|
|
31
|
+
carried the counter's name. Read the counter on the app's own connection, in
|
|
32
|
+
`bin/rails runner` or the console.
|
|
33
|
+
- **The packaged gem holds only what a user of the gem runs or reads.** That is
|
|
34
|
+
`lib/`, `exe/`, `app/`, `config/`, the README, the changelog, the license,
|
|
35
|
+
`SECURITY.md`, the user docs in `docs/`, and the files those docs link to
|
|
36
|
+
(`CONTRIBUTING.md` and three ADRs), so every link works in the unpacked gem.
|
|
37
|
+
5.35.2 also packed the files that serve the repository alone: `AGENTS.md`,
|
|
38
|
+
`CLAUDE.md`, `CONTEXT.md`, `CODE_OF_CONDUCT.md`, `docs/RELEASING.md`, the
|
|
39
|
+
other ADRs, `docs/agents/`, the docs site config, `.rspec`, `.rubocop.yml`,
|
|
40
|
+
`.ruby-version`, `Rakefile` and `server.json`. A tool that indexes an app's
|
|
41
|
+
bundle could read the two assistant files as instructions about the app.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- **On PostgreSQL a read with `TABLESAMPLE` runs.** The name of the sampling
|
|
46
|
+
method (`BERNOULLI`, `SYSTEM`) was read as a call of a VOLATILE function, and
|
|
47
|
+
the statement was refused. The statement never calls it, so it is no longer
|
|
48
|
+
read as a call. A VOLATILE function called anywhere in the statement, one
|
|
49
|
+
named like a sampling method included, is still refused. 5.35.2 refused the
|
|
50
|
+
read.
|
|
51
|
+
- **On PostgreSQL an `EXISTS` inside an `EXISTS` over a table with a secret
|
|
52
|
+
column runs.** The plan puts a Result node over the inner scan, and the plan
|
|
53
|
+
check counted every column of that scan as passed through, so the statement
|
|
54
|
+
was refused with a sentence saying it passed the secret column on. A Result
|
|
55
|
+
node is now read like a join: a column counts only where the Result names
|
|
56
|
+
it. A plain read of a few columns under a condition that holds no column
|
|
57
|
+
(`WHERE EXISTS (...)`) runs for the same reason. 5.35.2 refused them.
|
|
58
|
+
- **On PostgreSQL `EXPLAIN (GENERIC_PLAN)` answers the plan.** The plan check
|
|
59
|
+
planned the statement under it without the option, so a statement with `$1` in
|
|
60
|
+
it answered an SQL error. It is now planned with the option, read as the
|
|
61
|
+
server reads it: its last value counts, quoted or not. A server older than
|
|
62
|
+
PostgreSQL 16 has no such option and still answers its error. 5.35.2 answered
|
|
63
|
+
the error.
|
|
64
|
+
- **On PostgreSQL the `pg_sequences` view can be read.** It reads each
|
|
65
|
+
sequence's value through `pg_sequence_last_value`, a VOLATILE function that
|
|
66
|
+
writes nothing, and the statement was refused. The function now joins the
|
|
67
|
+
list of read-only VOLATILE functions. `nextval`, `setval`, `currval` and
|
|
68
|
+
`lastval` are still refused. 5.35.2 refused the view.
|
|
69
|
+
- **A query leaves a transaction the app opened with a raw `BEGIN` open, with
|
|
70
|
+
its writes.** Active Record does not count such a transaction, so on
|
|
71
|
+
PostgreSQL the query sent its own `BEGIN` and then `ROLLBACK`, which ended
|
|
72
|
+
the app's transaction and threw its writes away; the answer also missed the
|
|
73
|
+
rows the transaction had written. PostgreSQL now reads the server's own
|
|
74
|
+
transaction status and guards inside a savepoint there, under the same
|
|
75
|
+
read-only transaction and statement timeout. On MySQL and MariaDB the answer
|
|
76
|
+
is the refusal an open transaction gets ("MySQL cannot make a transaction
|
|
77
|
+
that is already open read-only"), where 5.35.2 answered the server's error
|
|
78
|
+
1568, and no `BEGIN`, which would commit the app's transaction, is sent. On
|
|
79
|
+
a SQLite file the statement runs on the app's own connection and reads the
|
|
80
|
+
transaction's rows, where 5.35.2 answered from the file without them.
|
|
81
|
+
5.35.2 and every earlier version ended the transaction on PostgreSQL.
|
|
82
|
+
- **On PostgreSQL a statement another session cancels is answered as the SQL
|
|
83
|
+
error it is.** A `pg_cancel_backend` from another session raises the same
|
|
84
|
+
error class as the statement timeout, and 5.35.2 answered "Query exceeded <N>
|
|
85
|
+
second timeout". The server's message tells the two apart, so a cancel now
|
|
86
|
+
answers `SQL error: PG::QueryCanceled: ERROR: canceling statement due to user
|
|
87
|
+
request`. A real timeout keeps its sentence. A server that writes its messages
|
|
88
|
+
in another language still answers a cancel as the timeout.
|
|
89
|
+
- **A query that reads a setting `rails_query` sets for each statement says so,
|
|
90
|
+
and gives the session's own value.** `SHOW statement_timeout` answered the
|
|
91
|
+
bound the tool set for the statement (`2s`) with nothing to say it was not the
|
|
92
|
+
session's (`50s`), and so did `transaction_read_only` on PostgreSQL, and the
|
|
93
|
+
statement limit (`max_execution_time` or `max_statement_time`) and
|
|
94
|
+
`lock_wait_timeout` on MySQL and MariaDB, by `@@` (the name backquoted or
|
|
95
|
+
not), by `SHOW VARIABLES` or by MariaDB's
|
|
96
|
+
`information_schema.SESSION_VARIABLES`. The answer now ends with a note, for
|
|
97
|
+
example "statement_timeout here is the value rails_query sets for each
|
|
98
|
+
statement it runs. The session's own: statement_timeout 50s." A name used as
|
|
99
|
+
an alias or a column, one in a string or a comment, and a global value on
|
|
100
|
+
MySQL and MariaDB get no note. 5.35.2 and earlier gave no note.
|
|
101
|
+
- **On PostgreSQL 13 and 14 a temporary table runs, with its secret column
|
|
102
|
+
filtered.** Before PostgreSQL 15 a plan names the session's temporary schema
|
|
103
|
+
by its number (`pg_temp_3`), where the column reads name it `pg_temp`, so
|
|
104
|
+
5.35.2 refused every statement over a temporary table there as one whose
|
|
105
|
+
columns could not be read. A whole row of a temporary table that holds a
|
|
106
|
+
secret column stays refused.
|
|
107
|
+
- **On SQLite a column whose own name starts with `pragma_` runs.** A
|
|
108
|
+
`pragma_*` name is now refused where SQLite reads it as the pragma function:
|
|
109
|
+
called, or where a table stands (after `FROM`, `JOIN` or `IN`, after a comma
|
|
110
|
+
of a `FROM` list, inside a parenthesis that opens one, or after a schema and a
|
|
111
|
+
dot there), bare, quoted or as a string. Anywhere else it is a column's or an
|
|
112
|
+
alias's name, and the statement runs. 5.35.1 and 5.35.2 refused such a name
|
|
113
|
+
wherever it stood, so `SELECT id FROM t WHERE pragma_flag = 1` was refused.
|
|
114
|
+
- **On SQLite a secret column name covers only the columns SQLite reads it as.**
|
|
115
|
+
SQLite folds ASCII letters alone, so `"é"` and `"É"` are two columns, and the
|
|
116
|
+
secret column rule now compares names that way there. With `"É"` in
|
|
117
|
+
`query_redacted_columns`, `SELECT "é" FROM t` runs and `SELECT *` filters `É`
|
|
118
|
+
alone; every earlier version refused `é` too and filtered both. A name is
|
|
119
|
+
still read with every letter folded for what it means, so a column that folds
|
|
120
|
+
to a built-in name or one the heuristic flags stays secret, and so does one a
|
|
121
|
+
configured name folds to when no column spells that name as SQLite folds it.
|
|
122
|
+
PostgreSQL, MySQL and MariaDB fold every letter as before.
|
|
123
|
+
- **On SQLite a statement that names a view over a pragma function or a
|
|
124
|
+
counter that tells of the connection is refused, by the view and the
|
|
125
|
+
function.** The view's definition runs on `rails_query`'s own connection, so
|
|
126
|
+
`CREATE VIEW v AS SELECT name FROM pragma_database_list` answered without
|
|
127
|
+
the app's attached databases, and a view calling `changes()` answered for the
|
|
128
|
+
wrong connection, both as a success. 5.35.1 and 5.35.2 refused the function
|
|
129
|
+
written in the statement alone. A view over a pragma function that describes
|
|
130
|
+
the schema or the database file, such as `pragma_table_info`, runs.
|
|
131
|
+
- **Every preset names each database's adapter and lists the databases.** On
|
|
132
|
+
`preset: standard`, which runs no `multi_database` introspector, the static
|
|
133
|
+
tier called the primary's adapter `unknown` in `rails_onboard` and in the
|
|
134
|
+
generated files, named no adapter beside a second database, and left the `-
|
|
135
|
+
Databases:` line out of CLAUDE.md and `.claude/rules/rails-context.md`. The
|
|
136
|
+
context now carries the app databases list on every preset, and where the
|
|
137
|
+
`multi_database` introspector failed, so these answer what `preset: full`
|
|
138
|
+
answers. 5.35.2 and earlier versions had the gap.
|
|
139
|
+
- **A model whose class body raises is named with what it raised.** Ruby
|
|
140
|
+
registers a class before its body runs, so a model file that raised partway
|
|
141
|
+
down its class was listed as `[VERIFIED]` with only what ran before the
|
|
142
|
+
raise, and with no word that loading it failed. It is now named with the
|
|
143
|
+
error, as a file that raises before its class opens has been since 5.35.2.
|
|
144
|
+
5.35.2 and earlier versions listed it.
|
|
145
|
+
- **A booted model keeps its STI hierarchy while nothing has queried its
|
|
146
|
+
database.** A model whose table is in a second database, and so not in
|
|
147
|
+
`db/schema.rb`, lost its STI parent, children and type column until a first
|
|
148
|
+
query connected that database. The table is now read as not known to lack the
|
|
149
|
+
type column, as the static tier reads it, and a model with no STI parent or
|
|
150
|
+
child still answers no STI. An abstract base, such as the class a second
|
|
151
|
+
database connects through, is no STI parent. 5.35.2 and earlier versions had
|
|
152
|
+
the gap.
|
|
153
|
+
- **The static mailer list adds the actions a mailer inherits from the app's own
|
|
154
|
+
mailers.** A `ShopMailer < BaseMailer` listed only the actions in its own
|
|
155
|
+
file, where Rails delivers the parent's public methods too and the booted tier
|
|
156
|
+
lists them. Each name now takes the visibility the nearest class gives it, so
|
|
157
|
+
a method the child makes private, under `private` or with `private :name`,
|
|
158
|
+
stays out. The methods of `ApplicationMailer` itself stay out too, as the
|
|
159
|
+
booted tier leaves them out, and on both tiers a mailer with no action of its
|
|
160
|
+
own names where they come from only for a parent past the app's own mailers,
|
|
161
|
+
such as `Devise::Mailer`, and none past `ApplicationMailer`. 5.35.2 and
|
|
162
|
+
earlier versions had the gap.
|
|
163
|
+
- **`rails_get_schema` says when two databases share `db/schema.rb`.** Rails
|
|
164
|
+
dumps both the first database and the one named `primary` to that file, so it
|
|
165
|
+
holds whichever was dumped last. A table of the other database was called
|
|
166
|
+
missing from the connected one, with advice to run `rails db:migrate`, which
|
|
167
|
+
changes nothing. The answer now says "_main and primary both dump to
|
|
168
|
+
db/schema.rb, so it may describe primary. Not in main: events._", and asked
|
|
169
|
+
for that table it says the same, with no word of a migration. 5.35.2 and
|
|
170
|
+
earlier versions had the gap.
|
|
171
|
+
- **The conventions section says how many entries each of its lists of five
|
|
172
|
+
leaves out.** `rails_get_config(section: "conventions")` shows the first five
|
|
173
|
+
authorization checks, denials, flash notices, flash alerts and not-found
|
|
174
|
+
patterns, and now ends a longer list with `- _...and N more._`. Every earlier
|
|
175
|
+
version cut each list at five and said nothing about the rest.
|
|
176
|
+
- **`rails_runtime_info` shows Sidekiq's queue counts as the app's only when
|
|
177
|
+
Sidekiq is the Active Job adapter.** Under another adapter the jobs section
|
|
178
|
+
says live queue stats are only read from Sidekiq and names that adapter. When
|
|
179
|
+
the app also has Sidekiq workers of its own, Sidekiq's counts follow under a
|
|
180
|
+
`### Sidekiq` heading that says they are those workers' counts. Since 4.3.1
|
|
181
|
+
any app with Sidekiq loaded got Sidekiq's counts under the adapter's name,
|
|
182
|
+
where they read as that adapter's queue.
|
|
183
|
+
|
|
184
|
+
## [5.35.2] - 2026-10-14
|
|
185
|
+
|
|
186
|
+
### Security
|
|
187
|
+
|
|
188
|
+
- **On MySQL and MariaDB a quote character inside a string does not change
|
|
189
|
+
which databases a statement is read to name.** The secret column rule reads
|
|
190
|
+
the columns of every database a statement names. In 5.35.1, the first release
|
|
191
|
+
with that read, a quote character inside a string could keep the rule from
|
|
192
|
+
seeing that the statement named another database. A secret column of a table
|
|
193
|
+
or of a view in that database then came back. Each name before a dot is now
|
|
194
|
+
read backward from its dot, so nothing written before the name changes how it
|
|
195
|
+
is read. The refusals are the ones 5.35.1 gives. This gap is in 5.35.1
|
|
196
|
+
alone: earlier versions read no other database, which the 5.35.1 notes cover.
|
|
197
|
+
- **On MySQL and MariaDB a `[` outside a string or a quoted name is refused.**
|
|
198
|
+
MariaDB reads `[name]` as a quoted name when the session's sql_mode holds
|
|
199
|
+
`MSSQL`, and the statement checks read a bracket as an ordinary character. In
|
|
200
|
+
a session with that mode the checks and the server could read one statement
|
|
201
|
+
two ways. `rails_query` does not read the session's mode, so it now refuses
|
|
202
|
+
the bracket on both servers. 5.35.1 and every earlier version had the gap,
|
|
203
|
+
on an app whose connection runs in that mode.
|
|
204
|
+
- **On MySQL and MariaDB a query leaves no value behind on the app's
|
|
205
|
+
connection.** A user variable assignment (`:=`) and `LAST_INSERT_ID` called
|
|
206
|
+
with an argument each set a value that stayed on the pooled connection after
|
|
207
|
+
the query returned, which a read-only transaction does not stop. Both are now
|
|
208
|
+
refused. 5.35.1 and every earlier version ran them.
|
|
209
|
+
- **On PostgreSQL what a foreign scan sends to the remote server is read like
|
|
210
|
+
what the plan runs itself.** A condition, a sort or a group on a secret
|
|
211
|
+
column of a `postgres_fdw` foreign table ran on the remote server, where the
|
|
212
|
+
plan checks did not read it. So a statement could test the column's value,
|
|
213
|
+
also through a view that renames the column and under a `column_name`
|
|
214
|
+
option. The remote statement the plan prints is now read, and such a
|
|
215
|
+
statement answers "Blocked: the query's plan uses the secret column
|
|
216
|
+
`<column>`". Fetching the column returns `[FILTERED]` as before. A statement
|
|
217
|
+
whose plan holds a foreign scan costs one more catalog read. Only the remote
|
|
218
|
+
statement of `postgres_fdw` is read: another foreign data wrapper prints
|
|
219
|
+
keys of its own. 5.35.1 and every earlier version had the gap.
|
|
220
|
+
- **On PostgreSQL a join that the remote server runs is traced to its
|
|
221
|
+
tables.** When `postgres_fdw` sends a whole join to the remote server, the
|
|
222
|
+
plan names the tables with their aliases in one line of the scan, which the
|
|
223
|
+
plan checks did not read, so the secret column rule did not reach those
|
|
224
|
+
tables. The tables and aliases of that line are now read like any scan's: a
|
|
225
|
+
secret column is `[FILTERED]` in place and a whole row is refused, as for a
|
|
226
|
+
local table. 5.35.1 and every earlier version had the gap.
|
|
227
|
+
- **On PostgreSQL the check for a secret column passed through a subquery
|
|
228
|
+
reads a name with two spaces in a row or a dotted capital I.** In 5.35.1
|
|
229
|
+
that check missed a secret column whose name holds either one. A statement
|
|
230
|
+
that passes such a column through a subquery, a CTE or a set operation is
|
|
231
|
+
now refused as for any other secret column. 5.35.0 refused it, so the gap is
|
|
232
|
+
in 5.35.1 alone.
|
|
233
|
+
- **On SQLite a virtual table built on a relation that is not read is refused
|
|
234
|
+
however its definition is written.** 5.35.1 read a relation's name in the
|
|
235
|
+
`content=` option of a full-text table and in the table a vocabulary lists.
|
|
236
|
+
A virtual table that named the relation another way ran, and returned the
|
|
237
|
+
rows or words of a refused view, a shadow table or the page modules. The
|
|
238
|
+
module and every argument can now name a relation, except the columns a
|
|
239
|
+
full-text table (fts3, fts4, fts5) declares and the type an `fts5vocab`
|
|
240
|
+
table ends with (`row`, `col` or `instance`). An argument with `=` is read
|
|
241
|
+
whole, whatever its name, and comments are taken out first. The module is
|
|
242
|
+
read after the table's own name, so a name that spells a module changes
|
|
243
|
+
nothing: 5.35.1 read such a name as the module, and the rule for a virtual
|
|
244
|
+
table that holds a secret column did not reach that table. The answers are
|
|
245
|
+
the ones 5.35.1 gives. The gap needs a virtual table the app itself created.
|
|
246
|
+
5.35.0 and earlier returned such a table's rows too.
|
|
247
|
+
- **On SQLite a relation whose name holds an apostrophe is matched where it
|
|
248
|
+
is written in single quotes.** SQLite reads a name in single quotes where a
|
|
249
|
+
table is expected, with the apostrophe doubled inside. The secret column
|
|
250
|
+
rule spelled a name in double quotes and in backticks and missed that
|
|
251
|
+
spelling, so a statement or a definition that used it was not held to the
|
|
252
|
+
rule for that relation. 5.35.1 and every earlier version had the gap, on an
|
|
253
|
+
app with a relation so named.
|
|
254
|
+
|
|
255
|
+
### Changed
|
|
256
|
+
|
|
257
|
+
The three refusals below go out under a patch number. Each closes a guard gap,
|
|
258
|
+
and the maintainer chose 5.35.2 for them, outside step 1 of `docs/RELEASING.md`.
|
|
259
|
+
|
|
260
|
+
- **On MySQL and MariaDB a statement with a user variable assignment (`:=`) is
|
|
261
|
+
refused.** The answer is "Blocked: a user variable assignment (`:=`) is not
|
|
262
|
+
run. It leaves a value on the pooled connection after the query returns,
|
|
263
|
+
which a read-only transaction does not stop." A statement that numbers its
|
|
264
|
+
rows with `@n := @n + 1` ran before and is refused now. Use a window
|
|
265
|
+
function, `ROW_NUMBER() OVER (...)`, which MySQL 8 and MariaDB 10.2 and
|
|
266
|
+
later have. A `:=` inside a string or a quoted name is data and runs, with
|
|
267
|
+
one case apart: a statement with a backslash before a quote inside a string.
|
|
268
|
+
The second reading of a backslash ends the string at that quote, and where
|
|
269
|
+
that leaves the `:=` outside a string the statement is refused. Write the
|
|
270
|
+
quote doubled.
|
|
271
|
+
- **On MySQL and MariaDB `LAST_INSERT_ID` with an argument is refused.** The
|
|
272
|
+
answer is "Blocked: LAST_INSERT_ID with an argument is not run. It sets a
|
|
273
|
+
value that stays on the pooled connection after the query returns, which a
|
|
274
|
+
read-only transaction does not stop. LAST_INSERT_ID() with no argument
|
|
275
|
+
runs." The call is matched on the raw text, like the session functions, so
|
|
276
|
+
it is refused inside a string too.
|
|
277
|
+
- **On MySQL and MariaDB a statement with a `[` outside a string or a quoted
|
|
278
|
+
name is refused.** The answer is "Blocked: a `[` outside a string or a
|
|
279
|
+
quoted name is not run. MariaDB reads it as the start of a quoted name when
|
|
280
|
+
the session's sql_mode holds MSSQL, and rails_query does not read the
|
|
281
|
+
session's mode. Quote a name in backticks." MySQL, and MariaDB outside that
|
|
282
|
+
mode, run no statement that holds one. In a session with that mode, write
|
|
283
|
+
`` `name` `` for `[name]`. A `[` inside a string or a backticked name runs,
|
|
284
|
+
with one case apart: a statement with a backslash before a quote inside a
|
|
285
|
+
string, as in `'O\'Brien'` beside `REGEXP '[a-z]'`. The second reading of a
|
|
286
|
+
backslash ends the string at that quote, and where that leaves the `[`
|
|
287
|
+
outside a string the statement is refused. Such a statement ran before.
|
|
288
|
+
Write the quote doubled.
|
|
289
|
+
|
|
290
|
+
### Fixed
|
|
291
|
+
|
|
292
|
+
- **A write that follows a `WITH` clause is refused as the write it is.**
|
|
293
|
+
`WITH ... INSERT INTO ...` answered "Blocked: SELECT INTO creates a table".
|
|
294
|
+
It now answers "Blocked: contains INSERT", and `REPLACE` and `MERGE` after a
|
|
295
|
+
`WITH` clause are named the same way. A real `SELECT ... INTO` keeps its
|
|
296
|
+
sentence.
|
|
297
|
+
- **A refusal names a view, a table and a relation as the database spells
|
|
298
|
+
them.** It named them case folded, with a run of spaces as one space, so a
|
|
299
|
+
view called `Hq Odd View` was named `hq odd view`, which no relation is
|
|
300
|
+
called. Two names that differ by a run of spaces alone are one name to the
|
|
301
|
+
rule, and the refusal names the first of them it read.
|
|
302
|
+
- **A view refused over a table that is not read is said to name it.** The
|
|
303
|
+
answer ended "its definition reads `<table>`, which is not read", and the
|
|
304
|
+
rule tests whether the definition names the table. It now ends "its
|
|
305
|
+
definition names `<table>`, which is not read".
|
|
306
|
+
- **The answer for a view that leads too deep drops its count.** It reads "The
|
|
307
|
+
view `<view>` leads through more databases than rails_query follows, so the
|
|
308
|
+
secret columns it could read cannot be told and no statement that names it
|
|
309
|
+
runs." The "(3 rounds)" after "follows" told a user nothing.
|
|
310
|
+
- **The note for a row limit of one says "1 row shown".** It said "1 rows
|
|
311
|
+
shown; the query returned more".
|
|
312
|
+
- **On PostgreSQL a sequence can be queried.** `SELECT last_value FROM
|
|
313
|
+
<sequence>` and `SELECT * FROM <sequence>` ran on 5.35.0, and 5.35.1 answered
|
|
314
|
+
that the sequence's columns could not be read. A sequence is now read like a
|
|
315
|
+
table that holds no secret column.
|
|
316
|
+
- **On PostgreSQL a whole row of a table with no secret column runs when
|
|
317
|
+
another table shares its alias or its name with a number after it.** 5.35.1
|
|
318
|
+
refused such a statement when a subquery used the same alias for a table
|
|
319
|
+
that holds a secret column, or when a table named like `name_2` stood beside
|
|
320
|
+
it, and the refusal named the other table. 5.35.0 ran it. The number the
|
|
321
|
+
planner adds to an alias is now taken off for the members of an Append or a
|
|
322
|
+
Merge Append alone, and a refusal over a whole row names the relation whose
|
|
323
|
+
row the statement reads.
|
|
324
|
+
- **On PostgreSQL a block comment inside a block comment is read as the server
|
|
325
|
+
reads it.** PostgreSQL nests block comments, and the gate ended one at the
|
|
326
|
+
first `*/`, so `SELECT 1 /* a /* b */ c */ AS n` answered PostgreSQL's
|
|
327
|
+
syntax error. It now runs.
|
|
328
|
+
- **On SQLite a statement that runs past `query_timeout` answers the timeout
|
|
329
|
+
when a writer takes the lock while it runs.** It answered that the database
|
|
330
|
+
was locked and the query did not run. The query process now takes its read
|
|
331
|
+
lock before it prepares the statement, so no lock stops the statement from
|
|
332
|
+
there on. The locked answer is given for a lock the query process is still
|
|
333
|
+
waiting on when it gives up or when the deadline passes.
|
|
334
|
+
- **On a SQLite file database a statement that names something the app's
|
|
335
|
+
connection alone holds runs on that connection.** While that connection
|
|
336
|
+
holds a temp object or an attachment, a statement runs there when it names a
|
|
337
|
+
temp object, `sqlite_temp_master` or `sqlite_temp_schema`, the `temp` schema
|
|
338
|
+
or an attached database, or calls a pragma function (`pragma_table_info(...)`,
|
|
339
|
+
`pragma_table_list` and the rest) or `dbstat`, since an argument can name any
|
|
340
|
+
of them.
|
|
341
|
+
The query process has none of them, so it answered an empty list, the rows of
|
|
342
|
+
a main table of the same name, or "unknown database". The answer now ends
|
|
343
|
+
with the note that the query ran without a time limit. A name is matched
|
|
344
|
+
anywhere in the statement, in any letter case and inside a string too.
|
|
345
|
+
Finding this out takes two more reads before a statement on a file database
|
|
346
|
+
where a query process can run: eight where there were six.
|
|
347
|
+
- **The full generated file lists the first database's recent migrations
|
|
348
|
+
under its own name.** With a first database not named `primary` and another
|
|
349
|
+
one that is, the heading read `### Recent Migrations: primary`. It now names
|
|
350
|
+
the first database, as in `### Recent Migrations: animals`.
|
|
351
|
+
- **The database marks and the first database's own name do not depend on the
|
|
352
|
+
preset.** On `preset: standard`, or any `introspectors` list without
|
|
353
|
+
`multi_database`, `rails_get_schema`, `rails_onboard`,
|
|
354
|
+
`rails_performance_check` and the generated files lost the mark of a
|
|
355
|
+
database another environment configures, such as `cache (production only)`,
|
|
356
|
+
and called a first database with another name `primary`. They now read the
|
|
357
|
+
app databases list there, as on `full`, and `rails_onboard` names a
|
|
358
|
+
secondary's adapter.
|
|
359
|
+
- **The model, controller, job, mailer and channel lists hold a class only
|
|
360
|
+
while its own constant names it.** A class whose constant was removed stays
|
|
361
|
+
loaded until the garbage collector takes it, so a list could hold it in one
|
|
362
|
+
run and lose it in the next. Such a class is now left out. A constant still
|
|
363
|
+
waiting to autoload counts as naming its class. The lists of a class's
|
|
364
|
+
children follow the same rule: a model's STI children, the classes a base job
|
|
365
|
+
or mailer is inherited by, and the abstract bases.
|
|
366
|
+
- **A model file that raises before its class opens costs that model alone.**
|
|
367
|
+
A failing `require` at the top of the file, or a raise above the class line,
|
|
368
|
+
made the whole models section answer an error. The file is now named with
|
|
369
|
+
what it raised, and the other models stay listed.
|
|
370
|
+
- **The refusal over a secret column says where `query_allowed_columns` is
|
|
371
|
+
set on either install.** It said to add the name "in an initializer", which
|
|
372
|
+
does nothing on a standalone install. It now ends "add "x" to
|
|
373
|
+
query_allowed_columns in .rails-ai-context.yml (or in
|
|
374
|
+
config/initializers/rails_ai_context.rb where the app bundles the gem)."
|
|
375
|
+
- **`rails_runtime_info` says when table sizes or index usage could not be
|
|
376
|
+
read.** A read that failed or ran out of time left its part of the database
|
|
377
|
+
section out with no word. The section now says "_Table sizes were not read:
|
|
378
|
+
the read failed or took longer than query_timeout._", and at `detail:
|
|
379
|
+
"full"` "_Index usage was not read: the read failed or took longer than
|
|
380
|
+
query_timeout._"
|
|
381
|
+
|
|
8
382
|
## [5.35.1] - 2026-10-13
|
|
9
383
|
|
|
10
384
|
### Security
|
|
@@ -8242,7 +8616,7 @@ Every introspector that scans `.rb` files has been migrated from regex-based pat
|
|
|
8242
8616
|
|
|
8243
8617
|
### Added - 8 new introspectors closing RAILS_NERVOUS_SYSTEM.md gaps
|
|
8244
8618
|
|
|
8245
|
-
An audit against
|
|
8619
|
+
An audit against `RAILS_NERVOUS_SYSTEM.md` identified 9 framework sections where introspection was missing or partial. This release ships the 8 introspectors needed to close them (the 9th - §11 Query interface - was already covered by `:conventions` via `load_async` / `.async_*` scanning). All are wired into `PRESETS[:full]`. The gem now exposes **39 introspectors** (up from 31).
|
|
8246
8620
|
|
|
8247
8621
|
- **`InitializerIntrospector` (`:initializers`, §2).** Enumerates `Rails.application.initializers` - every initializer's name, owner, declared `before:` / `after:` ordering edges, and the `source_location` of its block. Also summarizes each `config/initializers/*.rb` file (initializer count + the top `config.*` setters it touches) so AI can jump straight to user-owned boot code.
|
|
8248
8622
|
- **`AutoloadIntrospector` (`:autoload`, §3).** Zeitwerk presence, both `Rails.autoloaders.main` and `.once` with their collapsed dirs + ignored paths + root dirs, raw `autoload_paths` / `autoload_once_paths` / `eager_load_paths`, the resolved `eager_load` boolean, plus custom inflection rules extracted from `config/initializers/*.rb` (`inflect.acronym` / `.plural` / `.singular` / `.irregular` / `.uncountable` / `.human`). Paths are root-relative.
|
data/CONTRIBUTING.md
CHANGED
|
@@ -106,9 +106,10 @@ RAC_TEST_MYSQL_URL=trilogy://root:secret@127.0.0.1:3306/rac \
|
|
|
106
106
|
BUNDLE_GEMFILE=gemfiles/database_servers.gemfile bundle exec rspec spec/lib/rails_ai_context/database_family/database_servers_spec.rb
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
The specs create and drop tables and views whose names start with `rac_`, so point them at a scratch database.
|
|
109
|
+
The specs create and drop tables and views whose names start with `rac_`, and leave the `postgres_fdw` extension installed on PostgreSQL, so point them at a scratch database.
|
|
110
110
|
CI's `database_servers` job runs them on PostgreSQL 16 in every leg. Rails 8.0 runs two legs: MySQL 8.0 with MariaDB 10.6, and MySQL 8.4 with MariaDB 11.4. Rails 7.0 runs MySQL 8.0 with MariaDB 10.6, and Rails 8.1 runs MySQL 8.4 with MariaDB 11.4.
|
|
111
111
|
Rails 7.0 has no Trilogy adapter, so its leg runs PostgreSQL and mysql2 only.
|
|
112
|
+
The `postgresql_versions` job runs the PostgreSQL specs on PostgreSQL 13, 14, 15, 17 and 18 on Rails 8.0, since the plan checks read the plan the server prints.
|
|
112
113
|
The job builds mysql2 against MariaDB's client library (`libmariadb-dev`, through `BUNDLE_BUILD__MYSQL2`), which signs in to MySQL and to MariaDB. A MySQL client from 9.0 on cannot sign in to MariaDB.
|
|
113
114
|
|
|
114
115
|
### Static fixture
|
data/docs/COMPATIBILITY.md
CHANGED
|
@@ -312,6 +312,17 @@ Proof sources:
|
|
|
312
312
|
the tool's own checks timed on Ruby 3.4 for statements up to a megabyte
|
|
313
313
|
and on Ruby 3.1 for statements of 50 kilobytes; and the unit suite and the
|
|
314
314
|
E2E harness on Linux, macOS and Windows.
|
|
315
|
+
In the v5.35.2 closing review: the statement files of the v5.35.1 QA sent
|
|
316
|
+
through the tool again on PostgreSQL 16, MySQL 8.4, MariaDB 11.4 and SQLite,
|
|
317
|
+
on the v5.35.1 code and on the fixed code, with every changed answer traced
|
|
318
|
+
to its fix; and for each fix an example that fails on the v5.35.1 code, on a
|
|
319
|
+
real server where the fix is a server's.
|
|
320
|
+
In the v5.36.0 release QA: for each fix an example that fails on the v5.35.2
|
|
321
|
+
code, on a real server where the fix is a server's; `rails_query` through
|
|
322
|
+
its tool call in CI on PostgreSQL 16, MySQL 8.0 and 8.4 and MariaDB 10.6 and
|
|
323
|
+
11.4 on Rails 7.0 (mysql2), 8.0 and 8.1 (mysql2 and Trilogy), and on
|
|
324
|
+
PostgreSQL 13, 14, 15, 17 and 18 on Rails 8.0; and the unit suite and the
|
|
325
|
+
E2E harness on Linux, macOS and Windows.
|
|
315
326
|
2. Non-crash coverage for every built-in tool including `get_view` in
|
|
316
327
|
`spec/e2e/in_gemfile_install_spec.rb`'s full-tool sweep; output correctness
|
|
317
328
|
(ivar cross-check, render-form detection, partial interfaces) verified
|
|
@@ -358,7 +369,10 @@ row estimates and index usage say they are not available there. CI's
|
|
|
358
369
|
8.4 and MariaDB 10.6 and 11.4 on Rails 8.0, through mysql2 and Trilogy. It also
|
|
359
370
|
runs PostgreSQL 16 with MySQL 8.0 and MariaDB 10.6 on Rails 7.0, which has no
|
|
360
371
|
Trilogy adapter, and with MySQL 8.4 and MariaDB 11.4 on Rails 8.1; see
|
|
361
|
-
[CONTRIBUTING.md](../CONTRIBUTING.md#real-database-servers).
|
|
372
|
+
[CONTRIBUTING.md](../CONTRIBUTING.md#real-database-servers). The
|
|
373
|
+
`postgresql_versions` job runs the PostgreSQL examples on PostgreSQL 13, 14,
|
|
374
|
+
15, 17 and 18 as well, since the plan checks read the plan the server prints and
|
|
375
|
+
its shape changes between major versions.
|
|
362
376
|
|
|
363
377
|
Postgres itself - live query safety (read-only transactions, the
|
|
364
378
|
`BLOCKED_FUNCTIONS` guard against `pg_read_file`/`dblink`/`COPY ... PROGRAM`,
|