diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c63d53cd..ccaeeb904 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: run: | curl https://sh.rustup.rs -sSf | sh -s -- -y source ~/.cargo/env - cargo install cargo-pgrx --version "0.11.2" --locked + cargo install cargo-pgrx --version "0.12.9" --locked if [[ ! -d ~/.pgrx ]]; then cargo pgrx init diff --git a/.github/workflows/ubuntu-packages-and-docker-image.yml b/.github/workflows/ubuntu-packages-and-docker-image.yml index 985f589b5..36f137c88 100644 --- a/.github/workflows/ubuntu-packages-and-docker-image.yml +++ b/.github/workflows/ubuntu-packages-and-docker-image.yml @@ -72,11 +72,13 @@ jobs: libpq-dev \ libclang-dev \ wget \ + postgresql-17 \ postgresql-16 \ postgresql-15 \ postgresql-14 \ postgresql-13 \ postgresql-12 \ + postgresql-server-dev-17 \ postgresql-server-dev-16 \ postgresql-server-dev-15 \ postgresql-server-dev-14 \ @@ -98,13 +100,13 @@ jobs: with: working-directory: pgml-extension command: install - args: cargo-pgrx --version "0.11.2" --locked + args: cargo-pgrx --version "0.12.9" --locked - name: pgrx init uses: postgresml/gh-actions-cargo@master with: working-directory: pgml-extension command: pgrx - args: init --pg12=/usr/lib/postgresql/12/bin/pg_config --pg13=/usr/lib/postgresql/13/bin/pg_config --pg14=/usr/lib/postgresql/14/bin/pg_config --pg15=/usr/lib/postgresql/15/bin/pg_config --pg16=/usr/lib/postgresql/16/bin/pg_config + args: init --pg12=/usr/lib/postgresql/12/bin/pg_config --pg13=/usr/lib/postgresql/13/bin/pg_config --pg14=/usr/lib/postgresql/14/bin/pg_config --pg15=/usr/lib/postgresql/15/bin/pg_config --pg16=/usr/lib/postgresql/16/bin/pg_config --pg17=/usr/lib/postgresql/17/bin/pg_config - name: Build Postgres 12 uses: postgresml/gh-actions-cargo@master with: @@ -135,6 +137,12 @@ jobs: working-directory: pgml-extension command: pgrx args: package --pg-config /usr/lib/postgresql/16/bin/pg_config + - name: Build Postgres 17 + uses: postgresml/gh-actions-cargo@master + with: + working-directory: pgml-extension + command: pgrx + args: package --pg-config /usr/lib/postgresql/17/bin/pg_config - name: Build debs env: AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID }} diff --git a/pgml-cms/docs/open-source/pgml/developers/contributing.md b/pgml-cms/docs/open-source/pgml/developers/contributing.md index 146a0077b..16ec17fe5 100644 --- a/pgml-cms/docs/open-source/pgml/developers/contributing.md +++ b/pgml-cms/docs/open-source/pgml/developers/contributing.md @@ -67,7 +67,7 @@ Once there, you can initialize `pgrx` and get going: #### Pgrx command line and environments ```commandline -cargo install cargo-pgrx --version "0.11.2" --locked && \ +cargo install cargo-pgrx --version "0.12.9" --locked && \ cargo pgrx init # This will take a few minutes ``` diff --git a/pgml-cms/docs/open-source/pgml/developers/installation.md b/pgml-cms/docs/open-source/pgml/developers/installation.md index a0343f80e..734e0ac91 100644 --- a/pgml-cms/docs/open-source/pgml/developers/installation.md +++ b/pgml-cms/docs/open-source/pgml/developers/installation.md @@ -36,7 +36,7 @@ brew bundle PostgresML is written in Rust, so you'll need to install the latest compiler from [rust-lang.org](https://rust-lang.org). Additionally, we use the Rust PostgreSQL extension framework `pgrx`, which requires some initialization steps: ```bash -cargo install cargo-pgrx --version 0.11.2 && \ +cargo install cargo-pgrx --version 0.12.9 && \ cargo pgrx init ``` @@ -287,7 +287,7 @@ We use the `pgrx` Postgres Rust extension framework, which comes with its own in ```bash cd pgml-extension && \ -cargo install cargo-pgrx --version 0.11.2 && \ +cargo install cargo-pgrx --version 0.12.9 && \ cargo pgrx init ``` diff --git a/pgml-extension/Cargo.lock b/pgml-extension/Cargo.lock index 4f2c405ac..4adf22612 100644 --- a/pgml-extension/Cargo.lock +++ b/pgml-extension/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -26,6 +26,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "annotate-snippets" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e" +dependencies = [ + "unicode-width", + "yansi-term", +] + [[package]] name = "anstyle" version = "1.0.4" @@ -126,7 +136,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -154,7 +164,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -214,10 +224,29 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.46", + "syn 2.0.96", "which", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "annotate-snippets", + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "itertools 0.12.0", + "proc-macro2", + "quote 1.0.35", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.96", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -313,11 +342,43 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.21", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cargo_toml" -version = "0.16.3" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" +checksum = "a98356df42a2eb1bd8f1793ae4ee4de48e384dd974ce5eac8eee802edb7492be" dependencies = [ "serde", "toml", @@ -341,6 +402,16 @@ dependencies = [ "libc", ] +[[package]] +name = "cee-scape" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d67dfb052149f779f77e9ce089cea126e00657e8f0d11dafc7901fde4291101" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "cexpr" version = "0.6.0" @@ -379,12 +450,13 @@ dependencies = [ [[package]] name = "clap-cargo" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25122ca6ebad5f53578c26638afd9f0160426969970dc37ec6c363ff6b082ebd" +checksum = "23b2ea69cefa96b848b73ad516ad1d59a195cdf9263087d977f648a818c8b43e" dependencies = [ + "anstyle", + "cargo_metadata", "clap", - "doc-comment", ] [[package]] @@ -406,7 +478,7 @@ dependencies = [ "heck", "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -658,16 +730,7 @@ version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] @@ -691,18 +754,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -714,12 +765,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "either" version = "1.9.0" @@ -743,7 +788,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -773,9 +818,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -887,7 +932,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -965,6 +1010,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -984,12 +1038,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", - "hash32", + "hash32 0.2.1", "rustc_version 0.4.0", "spin", "stable_deref_trait", ] +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -1005,6 +1069,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hmac" version = "0.12.1" @@ -1087,6 +1157,23 @@ version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8573b2b1fb643a372c73b23f4da5f888677feef3305146d68a539250a9bccc7" +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + [[package]] name = "itertools" version = "0.10.5" @@ -1143,9 +1230,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -1190,7 +1277,7 @@ name = "lightgbm-sys" version = "0.3.0" source = "git+https://github.com/postgresml/lightgbm-rs?branch=main#978dd69f6c7aafb8500ecb255f2248fde80ebc97" dependencies = [ - "bindgen", + "bindgen 0.69.4", "cmake", "libc", ] @@ -1491,6 +1578,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.45" @@ -1582,7 +1675,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e5d8af0b707ac2fe1574daa88b4157da73b0de3dc7c39fe3e2c0bb64070501" dependencies = [ - "dirs 3.0.2", + "dirs", "openblas-build", "vcpkg", ] @@ -1610,7 +1703,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -1631,12 +1724,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "order-stat" version = "0.1.3" @@ -1645,9 +1732,13 @@ checksum = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" [[package]] name = "owo-colors" -version = "3.5.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" +dependencies = [ + "supports-color 2.1.0", + "supports-color 3.0.2", +] [[package]] name = "parking_lot" @@ -1724,7 +1815,8 @@ dependencies = [ "blas-src", "csv", "flate2", - "heapless", + "hash32 0.2.1", + "heapless 0.7.17", "indexmap 2.1.0", "itertools 0.12.0", "lightgbm", @@ -1753,22 +1845,21 @@ dependencies = [ [[package]] name = "pgrx" -version = "0.11.3" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2102faa5ef4a7bf096fefcf67692b293583efd18f9236340ad3169807dfc2b73" +checksum = "227bf7e162ce710994306a97bc56bb3fe305f21120ab6692e2151c48416f5c0d" dependencies = [ "atomic-traits", "bitflags 2.4.1", "bitvec", "enum-map", - "heapless", + "heapless 0.8.0", "libc", "once_cell", "pgrx-macros", "pgrx-pg-sys", "pgrx-sql-entity-graph", "seahash", - "seq-macro", "serde", "serde_cbor", "serde_json", @@ -1776,86 +1867,96 @@ dependencies = [ "uuid", ] +[[package]] +name = "pgrx-bindgen" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cbcd956c2da35baaf0a116e6f6a49a6c2fbc8f6b332f66d6fd060bfd00615f" +dependencies = [ + "bindgen 0.70.1", + "cc", + "clang-sys", + "eyre", + "pgrx-pg-config", + "proc-macro2", + "quote 1.0.35", + "shlex", + "syn 2.0.96", + "walkdir", +] + [[package]] name = "pgrx-macros" -version = "0.11.3" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c26810d09910ec987a6708d48d243efb5f879331e01c6fec0893714d0eb12bae" +checksum = "e2f4291450d65e4deb770ce57ea93e22353d97950566222429cd166ebdf6f938" dependencies = [ "pgrx-sql-entity-graph", "proc-macro2", "quote 1.0.35", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] name = "pgrx-pg-config" -version = "0.11.3" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0099ba4b635dfe1e34afc8bca8be43e9577c5d726aaf1dc7dd23a78f6c8a60" +checksum = "86a64a4c6e4e43e73cf8d3379d9533df98ded45c920e1ba8131c979633d74132" dependencies = [ "cargo_toml", - "dirs 5.0.1", "eyre", + "home", "owo-colors", "pathsearch", "serde", - "serde_derive", "serde_json", + "thiserror", "toml", "url", ] [[package]] name = "pgrx-pg-sys" -version = "0.11.3" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f40315259c41fede51eb23b791b48d0a112b0f47d0dcb6862b798d1fa1db6ea" +checksum = "63a5dc64f2a8226434118aa2c4700450fa42b04f29488ad98268848b21c1a4ec" dependencies = [ - "bindgen", - "clang-sys", - "eyre", + "cee-scape", "libc", - "memoffset", - "once_cell", + "pgrx-bindgen", "pgrx-macros", - "pgrx-pg-config", "pgrx-sql-entity-graph", - "proc-macro2", - "quote 1.0.35", "serde", - "shlex", "sptr", - "syn 1.0.109", - "walkdir", ] [[package]] name = "pgrx-sql-entity-graph" -version = "0.11.3" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d47a4e991c8c66162c5d6b0fc2bd382e43a58fc893ce05a6a15ddcb1bf7eee4" +checksum = "d81cc2e851c7e36b2f47c03e22d64d56c1d0e762fbde0039ba2cd490cfef3615" dependencies = [ "convert_case", "eyre", "petgraph", "proc-macro2", "quote 1.0.35", - "syn 1.0.109", + "syn 2.0.96", + "thiserror", "unescape", ] [[package]] name = "pgrx-tests" -version = "0.11.3" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3abc01e2bb930b072bd660d04c8eaa69a29d4727d5b2a641f946c603c1605e" +checksum = "0c2dd5d674cb7d92024709543da06d26723a2f7450c02083116b232587160929" dependencies = [ "clap-cargo", "eyre", "libc", - "once_cell", "owo-colors", + "paste", "pgrx", "pgrx-macros", "pgrx-pg-config", @@ -1973,14 +2074,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] name = "proc-macro2" -version = "1.0.74" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -2053,7 +2154,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -2066,7 +2167,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -2410,6 +2511,9 @@ name = "semver" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -2420,17 +2524,11 @@ dependencies = [ "pest", ] -[[package]] -name = "seq-macro" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" - [[package]] name = "serde" -version = "1.0.194" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -2447,13 +2545,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.194" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -2644,6 +2742,25 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "supports-color" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" +dependencies = [ + "is-terminal", + "is_ci", +] + +[[package]] +name = "supports-color" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] + [[package]] name = "syn" version = "0.11.11" @@ -2668,9 +2785,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.46" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote 1.0.35", @@ -2688,9 +2805,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.29.11" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" dependencies = [ "cfg-if", "core-foundation-sys", @@ -2698,7 +2815,7 @@ dependencies = [ "ntapi", "once_cell", "rayon", - "winapi", + "windows", ] [[package]] @@ -2771,7 +2888,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -2786,13 +2903,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -2808,10 +2926,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ + "num-conv", "time-core", ] @@ -2965,7 +3084,7 @@ checksum = "291db8a81af4840c10d636e047cac67664e343be44e24dfdbd1492df9a5d3390" dependencies = [ "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", ] [[package]] @@ -3013,6 +3132,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-xid" version = "0.0.4" @@ -3129,7 +3254,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", "wasm-bindgen-shared", ] @@ -3151,7 +3276,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote 1.0.35", - "syn 2.0.46", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3225,6 +3350,25 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3404,7 +3548,16 @@ name = "xgboost-sys" version = "0.2.0" source = "git+https://github.com/postgresml/rust-xgboost?branch=master#747631d5e50dcc9553f2a66988627f4ddec5b180" dependencies = [ - "bindgen", + "bindgen 0.69.4", "cmake", "libc", ] + +[[package]] +name = "yansi-term" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" +dependencies = [ + "winapi", +] diff --git a/pgml-extension/Cargo.toml b/pgml-extension/Cargo.toml index 64be2d004..d5644c41c 100644 --- a/pgml-extension/Cargo.toml +++ b/pgml-extension/Cargo.toml @@ -6,13 +6,18 @@ edition = "2021" [lib] crate-type = ["lib", "cdylib"] +[[bin]] +name = "pgrx_embed_pgml" +path = "./src/bin/pgrx_embed.rs" + [features] -default = ["pg16", "python"] +default = ["pg17", "python"] pg12 = ["pgrx/pg12", "pgrx-tests/pg12"] pg13 = ["pgrx/pg13", "pgrx-tests/pg13"] pg14 = ["pgrx/pg14", "pgrx-tests/pg14"] pg15 = ["pgrx/pg15", "pgrx-tests/pg15"] pg16 = ["pgrx/pg16", "pgrx-tests/pg16"] +pg17 = ["pgrx/pg17", "pgrx-tests/pg17"] use_as_lib = [] pg_test = [] python = ["pyo3"] @@ -26,6 +31,7 @@ blas = { version = "0.22" } blas-src = { version = "0.9", features = ["openblas"] } indexmap = { version = "2.1", features = ["serde"] } itertools = "0.12" +hash32 = { version = "=0.2.1" } heapless = "0.7" lightgbm = { git = "https://github.com/postgresml/lightgbm-rs", branch = "main" } linfa = { path = "deps/linfa" } @@ -39,8 +45,8 @@ openblas-src = { version = "0.10", features = ["cblas", "system"] } ndarray = { version = "0.15.6", features = ["serde", "blas"] } ndarray-stats = "0.5.1" parking_lot = "0.12" -pgrx = "=0.11.3" -pgrx-pg-sys = "=0.11.3" +pgrx = "=0.12.9" +pgrx-pg-sys = "=0.12.9" pyo3 = { version = "0.20.0", features = ["anyhow", "auto-initialize"], optional = true } rand = "0.8" rmp-serde = { version = "1.1" } @@ -51,7 +57,7 @@ typetag = "0.2" xgboost = { git = "https://github.com/postgresml/rust-xgboost", branch = "master" } [dev-dependencies] -pgrx-tests = "=0.11.3" +pgrx-tests = "=0.12.9" [build-dependencies] vergen = { version = "8", features = ["build", "git", "gitcl"] } diff --git a/pgml-extension/build.rs b/pgml-extension/build.rs index ca4ab1faf..9deb37eda 100644 --- a/pgml-extension/build.rs +++ b/pgml-extension/build.rs @@ -1,12 +1,14 @@ fn main() { + println!("cargo::rustc-check-cfg=cfg(pgrx_embed)"); + #[cfg(target_os = "macos")] { println!("cargo:rustc-link-search=/opt/homebrew/opt/openblas/lib"); println!("cargo:rustc-link-search=/opt/homebrew/opt/libomp/lib"); } - // PostgreSQL is using dlopen(RTLD_GLOBAL). this will parse some - // of symbols into the previous opened .so file, but the others will use a + // PostgreSQL is using dlopen(RTLD_GLOBAL). This will parse some + // of the symbols into the previous opened .so file, but the others will use a // relative offset in pgml.so, and will cause a null-pointer crash. // // hide all symbol to avoid symbol conflicts. diff --git a/pgml-extension/rust-toolchain.toml b/pgml-extension/rust-toolchain.toml index c6e4d7d50..efd9dc3db 100644 --- a/pgml-extension/rust-toolchain.toml +++ b/pgml-extension/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.79" +channel = "1.84.0" diff --git a/pgml-extension/src/api.rs b/pgml-extension/src/api.rs index 923c6fc70..868345fa5 100644 --- a/pgml-extension/src/api.rs +++ b/pgml-extension/src/api.rs @@ -233,7 +233,7 @@ fn train_joint( algorithm }; - // # Default repeatable random state when possible + // TODO Default repeatable random state when possible // let algorithm = Model.algorithm_from_name_and_task(algorithm, task); // if "random_state" in algorithm().get_params() and "random_state" not in hyperparams: // hyperparams["random_state"] = 0 @@ -599,9 +599,10 @@ pub fn embed(transformer: &str, text: &str, kwargs: default!(JsonB, "'{}'")) -> #[pg_extern(immutable, parallel_safe, name = "embed")] pub fn embed_batch( transformer: &str, - inputs: Vec<&str>, + inputs: Array<&str>, kwargs: default!(JsonB, "'{}'"), ) -> SetOfIterator<'static, Vec> { + let inputs: Vec<&str> = inputs.iter().map(|x| x.unwrap()).collect(); match crate::bindings::transformers::embed(transformer, inputs, &kwargs.0) { Ok(output) => SetOfIterator::new(output), Err(e) => error!("{e}"), @@ -613,9 +614,10 @@ pub fn embed_batch( pub fn rank( transformer: &str, query: &str, - documents: Vec<&str>, + documents: Array<&str>, kwargs: default!(JsonB, "'{}'"), ) -> TableIterator<'static, (name!(corpus_id, i64), name!(score, f64), name!(text, Option))> { + let documents: Vec<&str> = documents.iter().map(|x| x.unwrap()).collect(); match crate::bindings::transformers::rank(transformer, query, documents, &kwargs.0) { Ok(output) => TableIterator::new(output.into_iter().map(|x| (x.corpus_id, x.score, x.text))), Err(e) => error!("{e}"), @@ -671,13 +673,14 @@ pub fn chunk( pub fn transform_json( task: JsonB, args: default!(JsonB, "'{}'"), - inputs: default!(Vec<&str>, "ARRAY[]::TEXT[]"), + inputs: default!(Array<&str>, "ARRAY[]::TEXT[]"), cache: default!(bool, false), ) -> JsonB { if let Err(err) = crate::bindings::transformers::whitelist::verify_task(&task.0) { error!("{err}"); } + let inputs: Vec<&str> = inputs.iter().map(|x| x.unwrap()).collect(); match crate::bindings::transformers::transform(&task.0, &args.0, inputs) { Ok(output) => JsonB(output), Err(e) => error!("{e}"), @@ -690,13 +693,14 @@ pub fn transform_json( pub fn transform_string( task: String, args: default!(JsonB, "'{}'"), - inputs: default!(Vec<&str>, "ARRAY[]::TEXT[]"), + inputs: default!(Array<&str>, "ARRAY[]::TEXT[]"), cache: default!(bool, false), ) -> JsonB { let task_json = json!({ "task": task }); if let Err(err) = crate::bindings::transformers::whitelist::verify_task(&task_json) { error!("{err}"); } + let inputs: Vec<&str> = inputs.iter().map(|x| x.unwrap()).collect(); match crate::bindings::transformers::transform(&task_json, &args.0, inputs) { Ok(output) => JsonB(output), Err(e) => error!("{e}"), @@ -821,15 +825,17 @@ pub fn transform_stream_conversational_string( #[cfg(feature = "python")] #[pg_extern(immutable, parallel_safe, name = "generate")] fn generate(project_name: &str, inputs: &str, config: default!(JsonB, "'{}'")) -> String { - generate_batch(project_name, Vec::from([inputs]), config) - .first() - .unwrap() - .to_string() + let inputs: Vec<&str> = Vec::from([inputs]); + match crate::bindings::transformers::generate(Project::get_deployed_model_id(project_name), inputs, config) { + Ok(output) => output.first().unwrap().to_string(), + Err(e) => error!("{e}"), + } } #[cfg(feature = "python")] #[pg_extern(immutable, parallel_safe, name = "generate")] -fn generate_batch(project_name: &str, inputs: Vec<&str>, config: default!(JsonB, "'{}'")) -> Vec { +fn generate_batch(project_name: &str, inputs: Array<&str>, config: default!(JsonB, "'{}'")) -> Vec { + let inputs: Vec<&str> = inputs.iter().map(|x| x.unwrap()).collect(); match crate::bindings::transformers::generate(Project::get_deployed_model_id(project_name), inputs, config) { Ok(output) => output, Err(e) => error!("{e}"), diff --git a/pgml-extension/src/bin/pgrx_embed.rs b/pgml-extension/src/bin/pgrx_embed.rs new file mode 100644 index 000000000..5f5c4d858 --- /dev/null +++ b/pgml-extension/src/bin/pgrx_embed.rs @@ -0,0 +1 @@ +::pgrx::pgrx_embed!(); diff --git a/pgml-extension/src/bindings/langchain/mod.rs b/pgml-extension/src/bindings/langchain/mod.rs index 75d94914e..25039b23e 100644 --- a/pgml-extension/src/bindings/langchain/mod.rs +++ b/pgml-extension/src/bindings/langchain/mod.rs @@ -1,5 +1,4 @@ use anyhow::Result; -use pgrx::*; use pyo3::prelude::*; use pyo3::types::PyTuple; diff --git a/pgml-extension/src/bindings/xgboost.rs b/pgml-extension/src/bindings/xgboost.rs index 26a1e1ea2..7c29d03dd 100644 --- a/pgml-extension/src/bindings/xgboost.rs +++ b/pgml-extension/src/bindings/xgboost.rs @@ -295,7 +295,10 @@ fn fit(dataset: &Dataset, hyperparams: &Hyperparams, objective: learning::Object }, None => false, }; - Ok(Box::new(Estimator { softmax_objective, estimator: booster })) + Ok(Box::new(Estimator { + softmax_objective, + estimator: booster, + })) } pub struct Estimator { @@ -383,6 +386,9 @@ impl Bindings for Estimator { _ => false, }; - Ok(Box::new(Estimator { softmax_objective, estimator })) + Ok(Box::new(Estimator { + softmax_objective, + estimator, + })) } } diff --git a/pgml-extension/src/lib.rs b/pgml-extension/src/lib.rs index 1eab45ae7..7b13cc213 100644 --- a/pgml-extension/src/lib.rs +++ b/pgml-extension/src/lib.rs @@ -19,7 +19,7 @@ pub mod vectors; #[cfg(not(feature = "use_as_lib"))] pg_module_magic!(); -extension_sql_file!("../sql/schema.sql", name = "schema"); +extension_sql_file!("../sql/schema.sql", name = "schema", finalize); #[cfg(not(feature = "use_as_lib"))] #[pg_guard] diff --git a/pgml-extension/src/orm/model.rs b/pgml-extension/src/orm/model.rs index 333969d02..7b6aeb25d 100644 --- a/pgml-extension/src/orm/model.rs +++ b/pgml-extension/src/orm/model.rs @@ -13,7 +13,7 @@ use itertools::{izip, Itertools}; use ndarray::ArrayView1; use once_cell::sync::Lazy; use pgrx::heap_tuple::PgHeapTuple; -use pgrx::*; +use pgrx::{datum::*, *}; use rand::prelude::SliceRandom; use serde_json::json; @@ -1079,52 +1079,52 @@ impl Model { } // TODO handle NULL to NaN for arrays pgrx_pg_sys::BOOLARRAYOID => { - let element: Result>, TryFromDatumError> = + let element: Result>, TryFromDatumError> = tuple.get_by_index(index); - for j in element.as_ref().unwrap().as_ref().unwrap() { - features.push(*j as i8 as f32); + for j in element.unwrap().unwrap() { + features.push(j.unwrap() as i8 as f32); } } pgrx_pg_sys::INT2ARRAYOID => { - let element: Result>, TryFromDatumError> = + let element: Result>, TryFromDatumError> = tuple.get_by_index(index); - for j in element.as_ref().unwrap().as_ref().unwrap() { - features.push(*j as f32); + for j in element.unwrap().unwrap() { + features.push(j.unwrap() as f32); } } pgrx_pg_sys::INT4ARRAYOID => { - let element: Result>, TryFromDatumError> = + let element: Result>, TryFromDatumError> = tuple.get_by_index(index); - for j in element.as_ref().unwrap().as_ref().unwrap() { - features.push(*j as f32); + for j in element.unwrap().unwrap() { + features.push(j.unwrap() as f32); } } pgrx_pg_sys::INT8ARRAYOID => { - let element: Result>, TryFromDatumError> = + let element: Result>, TryFromDatumError> = tuple.get_by_index(index); - for j in element.as_ref().unwrap().as_ref().unwrap() { - features.push(*j as f32); + for j in element.unwrap().unwrap() { + features.push(j.unwrap() as f32); } } pgrx_pg_sys::FLOAT4ARRAYOID => { - let element: Result>, TryFromDatumError> = + let element: Result>, TryFromDatumError> = tuple.get_by_index(index); - for j in element.as_ref().unwrap().as_ref().unwrap() { - features.push(*j); + for j in element.unwrap().unwrap() { + features.push(j.unwrap()); } } pgrx_pg_sys::FLOAT8ARRAYOID => { - let element: Result>, TryFromDatumError> = + let element: Result>, TryFromDatumError> = tuple.get_by_index(index); - for j in element.as_ref().unwrap().as_ref().unwrap() { - features.push(*j as f32); + for j in element.unwrap().unwrap() { + features.push(j.unwrap() as f32); } } pgrx_pg_sys::NUMERICARRAYOID => { - let element: Result>, TryFromDatumError> = + let element: Result>, TryFromDatumError> = tuple.get_by_index(index); - for j in element.as_ref().unwrap().as_ref().unwrap() { - features.push(j.clone().try_into().unwrap()); + for j in element.unwrap().unwrap() { + features.push(j.unwrap().try_into().unwrap()); } } _ => error!( diff --git a/pgml-extension/src/orm/project.rs b/pgml-extension/src/orm/project.rs index ea23ba80e..3988f23f8 100644 --- a/pgml-extension/src/orm/project.rs +++ b/pgml-extension/src/orm/project.rs @@ -3,12 +3,88 @@ use std::collections::HashMap; use std::fmt::{Display, Error, Formatter}; use std::str::FromStr; +use hash32::{BuildHasherDefault, FnvHasher}; +use heapless::IndexMap; use once_cell::sync::Lazy; -use pgrx::*; +use pgrx::{datum::*, *}; // Use FnvHasher directly instead of dyn Hasher use crate::orm::*; -static PROJECT_ID_TO_DEPLOYED_MODEL_ID: PgLwLock> = PgLwLock::new(); +// We need a wrapper to implement PGRXSharedMemory for IndexMap +#[derive(Default)] +pub struct ProjectIdMap(IndexMap, 1024>); + +unsafe impl PGRXSharedMemory for ProjectIdMap {} + +impl ProjectIdMap { + pub fn new() -> Self { + Self(IndexMap::new()) + } + + pub fn insert(&mut self, project_id: i64, model_id: i64) -> Option { + self.0.insert(project_id, model_id).unwrap() + } + + pub fn get(&self, project_id: &i64) -> Option { + self.0.get(project_id).copied() + } + + pub fn clear(&mut self) { + self.0.clear() + } + + pub fn len(&self) -> usize { + self.0.len() + } +} + +// Wrapper for the PgLwLock +pub struct ProjectDeploymentMap(PgLwLock); + +impl ProjectDeploymentMap { + pub const fn new() -> Self { + Self(PgLwLock::new()) + } + + pub fn insert(&'static self, project_id: i64, model_id: i64) -> Option { + self.0.exclusive().insert(project_id, model_id) + } + + pub fn get(&'static self, project_id: &i64) -> Option { + self.0.share().get(project_id) + } + + pub fn clear(&'static self) { + self.0.exclusive().clear() + } + + pub fn len(&'static self) -> usize { + self.0.share().len() + } + + pub fn lock(&'static self) -> &'static PgLwLock { + &self.0 + } +} + +// Implement the required traits for our wrapper +unsafe impl PGRXSharedMemory for ProjectDeploymentMap {} + +impl PgSharedMemoryInitialization for ProjectDeploymentMap { + fn pg_init(&'static self) { + PgSharedMem::pg_init_locked(&self.0); + } + + unsafe fn shmem_init(&'static self) { + unsafe { + PgSharedMem::shmem_init_locked(&self.0); + } + } +} + +// Static declaration +static PROJECT_ID_TO_DEPLOYED_MODEL_ID: ProjectDeploymentMap = ProjectDeploymentMap::new(); + static PROJECT_NAME_TO_PROJECT_ID: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); /// Initialize shared memory. @@ -61,7 +137,7 @@ impl Project { let model_id = model_id .unwrap_or_else(|| error!("No deployed model exists for the project named: `{}`", project_name)); projects.insert(project_name.to_string(), project_id); - let mut projects = PROJECT_ID_TO_DEPLOYED_MODEL_ID.exclusive(); + let mut projects = PROJECT_ID_TO_DEPLOYED_MODEL_ID.0.exclusive(); if projects.len() == 1024 { warning!("Active projects have exceeded capacity map, clearing caches."); projects.clear(); @@ -70,7 +146,7 @@ impl Project { project_id } }; - *PROJECT_ID_TO_DEPLOYED_MODEL_ID.share().get(&project_id).unwrap() + PROJECT_ID_TO_DEPLOYED_MODEL_ID.0.share().get(&project_id).unwrap() } pub fn deploy(&self, model_id: i64, strategy: Strategy) { @@ -82,13 +158,13 @@ impl Project { (PgBuiltInOids::INT8OID.oid(), model_id.into_datum()), (PgBuiltInOids::TEXTOID.oid(), strategy.to_string().into_datum()), ], - ).unwrap(); - let mut projects = PROJECT_ID_TO_DEPLOYED_MODEL_ID.exclusive(); + ).expect("Deployment to be insertable"); + let mut projects = PROJECT_ID_TO_DEPLOYED_MODEL_ID.0.exclusive(); if projects.len() == 1024 { warning!("Active projects has exceeded capacity map, clearing caches."); projects.clear(); } - projects.insert(self.id, model_id).unwrap(); + projects.insert(self.id, model_id); } pub fn find(id: i64) -> Option { diff --git a/pgml-extension/src/orm/snapshot.rs b/pgml-extension/src/orm/snapshot.rs index 7b1db546a..15e548571 100644 --- a/pgml-extension/src/orm/snapshot.rs +++ b/pgml-extension/src/orm/snapshot.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use indexmap::IndexMap; use ndarray::Zip; -use pgrx::*; +use pgrx::{datum::*, *}; use serde::{Deserialize, Serialize}; use serde_json::json; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy