|
20 | 20 | from rhosocial.activerecord.backend.expression.statements.ddl_table import IndexDefinition |
21 | 21 |
|
22 | 22 | from rhosocial.activerecord.backend.expression.statements.ddl_index import ( |
| 23 | + CreateFulltextIndexExpression, |
23 | 24 | CreateIndexExpression, |
| 25 | + DropFulltextIndexExpression, |
24 | 26 | DropIndexExpression, |
25 | 27 | ) |
26 | 28 | from rhosocial.activerecord.backend.impl.postgres.dialect import PostgresDialect |
@@ -472,32 +474,104 @@ def test_get_supported_index_types(self): |
472 | 474 | assert types == ["BTREE", "HASH", "GIST", "GIN", "SPGIST", "BRIN"] |
473 | 475 |
|
474 | 476 |
|
475 | | -class TestFulltextNotSupported: |
476 | | - """PostgreSQL does not support MySQL-style FULLTEXT indexes.""" |
| 477 | +class TestFulltextDdlNotSupportedSearchSupported: |
| 478 | + """PostgreSQL full-text search via tsvector/tsquery, not MySQL-style DDL. |
| 479 | +
|
| 480 | + PostgreSQL does NOT support ``CREATE FULLTEXT INDEX`` (MySQL-style) — |
| 481 | + it uses ``CREATE INDEX ... USING GIN (to_tsvector(...))`` instead. |
| 482 | + However, full-text *search querying* IS supported through |
| 483 | + ``tsvector @@ tsquery`` operators, so the dialect reports |
| 484 | + ``supports_fulltext_search()`` True independently of the DDL capability. |
| 485 | + """ |
| 486 | + |
| 487 | + # --- DDL capability (MySQL-style CREATE FULLTEXT INDEX) --- |
477 | 488 |
|
478 | 489 | def test_supports_fulltext_index(self): |
479 | 490 | assert PostgresDialect().supports_fulltext_index() is False |
480 | 491 |
|
481 | 492 | def test_supports_fulltext_parser(self): |
482 | 493 | assert PostgresDialect().supports_fulltext_parser() is False |
483 | 494 |
|
484 | | - def test_supports_fulltext_boolean_mode(self): |
485 | | - assert PostgresDialect().supports_fulltext_boolean_mode() is False |
486 | | - |
487 | 495 | def test_supports_fulltext_query_expansion(self): |
488 | 496 | assert PostgresDialect().supports_fulltext_query_expansion() is False |
489 | 497 |
|
490 | | - def test_format_fulltext_match_raises(self): |
491 | | - with pytest.raises(UnsupportedFeatureError): |
492 | | - PostgresDialect().format_fulltext_match(["col"], "search") |
| 498 | + # --- Query capability (tsvector @@ tsquery) --- |
| 499 | + |
| 500 | + def test_supports_fulltext_search(self): |
| 501 | + assert PostgresDialect().supports_fulltext_search() is True |
| 502 | + |
| 503 | + def test_supports_fulltext_boolean_mode(self): |
| 504 | + # to_tsquery() supports &, |, ! — equivalent to MySQL BOOLEAN MODE. |
| 505 | + assert PostgresDialect().supports_fulltext_boolean_mode() is True |
| 506 | + |
| 507 | + def test_format_fulltext_match_natural_language(self): |
| 508 | + sql, params = PostgresDialect().format_fulltext_match(["col"], "search") |
| 509 | + assert "to_tsvector" in sql |
| 510 | + assert "plainto_tsquery" in sql |
| 511 | + assert "@@" in sql |
| 512 | + assert params == ("search",) |
| 513 | + |
| 514 | + def test_format_fulltext_match_boolean(self): |
| 515 | + sql, params = PostgresDialect().format_fulltext_match(["col"], "a & b", mode="BOOLEAN") |
| 516 | + assert "to_tsvector" in sql |
| 517 | + assert "to_tsquery" in sql |
| 518 | + assert "plainto_tsquery" not in sql |
| 519 | + assert params == ("a & b",) |
| 520 | + |
| 521 | + def test_format_fulltext_match_multi_column(self): |
| 522 | + sql, params = PostgresDialect().format_fulltext_match(["a", "b"], "search") |
| 523 | + assert " || " in sql |
| 524 | + assert "to_tsvector" in sql |
| 525 | + assert params == ("search",) |
| 526 | + |
| 527 | + def test_format_create_fulltext_index_statement_uses_gin(self): |
| 528 | + dialect = PostgresDialect() |
| 529 | + expr = CreateFulltextIndexExpression( |
| 530 | + dialect, |
| 531 | + index_name="idx_ft", |
| 532 | + table_name="articles", |
| 533 | + columns=["body"], |
| 534 | + ) |
| 535 | + sql, params = dialect.format_create_fulltext_index_statement(expr) |
| 536 | + assert sql.startswith("CREATE INDEX") |
| 537 | + assert "USING GIN" in sql |
| 538 | + assert "to_tsvector" in sql |
| 539 | + assert "articles" in sql |
| 540 | + assert params == () |
| 541 | + |
| 542 | + def test_format_create_fulltext_index_statement_if_not_exists(self): |
| 543 | + dialect = PostgresDialect() |
| 544 | + expr = CreateFulltextIndexExpression( |
| 545 | + dialect, |
| 546 | + index_name="idx_ft", |
| 547 | + table_name="articles", |
| 548 | + columns=["body"], |
| 549 | + if_not_exists=True, |
| 550 | + ) |
| 551 | + sql, _ = dialect.format_create_fulltext_index_statement(expr) |
| 552 | + assert "IF NOT EXISTS" in sql |
493 | 553 |
|
494 | | - def test_format_create_fulltext_index_statement_raises(self): |
495 | | - with pytest.raises(UnsupportedFeatureError): |
496 | | - PostgresDialect().format_create_fulltext_index_statement(None) |
| 554 | + def test_format_drop_fulltext_index_statement(self): |
| 555 | + dialect = PostgresDialect() |
| 556 | + expr = DropFulltextIndexExpression( |
| 557 | + dialect, |
| 558 | + index_name="idx_ft", |
| 559 | + table_name="articles", |
| 560 | + ) |
| 561 | + sql, _ = dialect.format_drop_fulltext_index_statement(expr) |
| 562 | + assert sql.startswith("DROP INDEX") |
| 563 | + assert "idx_ft" in sql |
497 | 564 |
|
498 | | - def test_format_drop_fulltext_index_statement_raises(self): |
499 | | - with pytest.raises(UnsupportedFeatureError): |
500 | | - PostgresDialect().format_drop_fulltext_index_statement(None) |
| 565 | + def test_format_drop_fulltext_index_statement_if_exists(self): |
| 566 | + dialect = PostgresDialect() |
| 567 | + expr = DropFulltextIndexExpression( |
| 568 | + dialect, |
| 569 | + index_name="idx_ft", |
| 570 | + table_name="articles", |
| 571 | + if_exists=True, |
| 572 | + ) |
| 573 | + sql, _ = dialect.format_drop_fulltext_index_statement(expr) |
| 574 | + assert "IF EXISTS" in sql |
501 | 575 |
|
502 | 576 |
|
503 | 577 | class TestCreateIndexExpression: |
|
0 commit comments