Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change Pre-Fetch #24

Merged
merged 14 commits into from
Dec 17, 2024
38 changes: 21 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,36 @@
[![GitHub license](https://img.shields.io/github/license/byjg/php-anydataset-db.svg)](https://opensource.byjg.com/opensource/licensing.html)
[![GitHub release](https://img.shields.io/github/release/byjg/php-anydataset-db.svg)](https://github.com/byjg/php-anydataset-db/releases/)

Anydataset Database Relational abstraction. Anydataset is an agnostic data source abstraction layer in PHP.
**AnyDataset-DB** provides a relational database abstraction layer. It is part of the Anydataset project, an agnostic
data source abstraction layer for PHP.

See more about Anydataset [here](https://opensource.byjg.com/anydataset).
Learn more about Anydataset [here](https://opensource.byjg.com/anydataset).

## Features

- Connection based on URI
- Support and fix code tricks with several databases (MySQL, PostgresSql, MS SQL Server, etc)
- Natively supports Query Cache by implementing a PSR-6 interface
- Supports Connection Routes based on regular expression against the queries, that's mean a select in a table should be
executed in a database and in another table should be executed in another (even if in different DB)
- Handles compatibility and code optimization across multiple databases (e.g., MySQL, PostgreSQL, MS SQL Server)
- Built-in Query Cache support using a PSR-6 compliant interface
- Enables connection routing based on regular expressions for queries (e.g., directing queries to different databases
for specific tables)

## Connection Based on URI

The connection string for databases is based on URL.
Database connections are defined using URL-based connection strings.

See below the current implemented drivers:
Supported drivers are listed below:

| Database | Connection String | Factory |
|---------------------|---------------------------------------------------|---------------------------|
| Sqlite | sqlite:///path/to/file | getDbRelationalInstance() |
| MySql/MariaDb | mysql://username:password@hostname:port/database | getDbRelationalInstance() |
| Postgres | psql://username:password@hostname:port/database | getDbRelationalInstance() |
| Sql Server (DbLib) | dblib://username:password@hostname:port/database | getDbRelationalInstance() |
| Sql Server (Sqlsrv) | sqlsrv://username:password@hostname:port/database | getDbRelationalInstance() |
| Oracle (OCI8) | oci8://username:password@hostname:port/database | getDbRelationalInstance() |
| Generic PDO | pdo://username:password@pdo_driver?PDO_PARAMETERS | getDbRelationalInstance() |
| Database | Connection String | Factory Method |
|---------------------|---------------------------------------------------|-----------------------------|
| SQLite | sqlite:///path/to/file | `getDbRelationalInstance()` |
| MySQL/MariaDB | mysql://username:password@hostname:port/database | `getDbRelationalInstance()` |
| PostgreSQL | psql://username:password@hostname:port/database | `getDbRelationalInstance()` |
| SQL Server (DbLib) | dblib://username:password@hostname:port/database | `getDbRelationalInstance()` |
| SQL Server (Sqlsrv) | sqlsrv://username:password@hostname:port/database | `getDbRelationalInstance()` |
| Oracle (OCI8) | oci8://username:password@hostname:port/database | `getDbRelationalInstance()` |
| Generic PDO | pdo://username:password@pdo_driver?PDO_PARAMETERS | `getDbRelationalInstance()` |

Example usage:

```php
<?php
Expand All @@ -56,6 +59,7 @@ $conn = \ByJG\AnyDataset\Db\Factory::getDbInstance("mysql://root:[email protected]
- [Generic PDO Driver](docs/generic-pdo-driver.md)
- [Running Tests](docs/tests.md)
- [Getting an Iterator from an existing PDO Statament](docs/pdostatement.md)
- [Pre Fetch records](docs/prefetch.md)

## Database Specifics

Expand Down
20 changes: 11 additions & 9 deletions docs/cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
sidebar_position: 4
---

# Cache results
# Cache Results

You can easily cache your results to speed up the results of long queries;
You need to add to your project an implementation of PSR-16. We suggested you add "byjg/cache".
You can easily cache query results to improve performance, especially for long-running queries.
To enable caching, you need to include a PSR-16 compliant caching library in your project.
We recommend using the `byjg/cache` library.

Also, you need to use the `SqlStatement` class to prepare the query and cache the results.
Additionally, you must use the `SqlStatement` class to prepare the query and cache the results.

```php
<?php
Expand All @@ -23,10 +24,11 @@ $sql->withCache($cache, 'my_cache_key', 60);
$iterator = $sql->getIterator($dbDriver, ['param' => 'value']);
```

**NOTES**
## Notes

- It will be saved one cache entry for each different parameters.
e.g. `['param' => 'value']` and `['param' => 'value2']` will have one entry for each result.
- **One cache entry per parameter set:** A separate cache entry will be created for each unique set of parameters.
For example:
- `['param' => 'value']` and `['param' => 'value2']` will result in two distinct cache entries.

- If you use the same key for different sql statements it will not differentiate one from another and
you can get unexpected results
- **Key uniqueness:** If you use the same cache key for different SQL statements, they will not be differentiated. This
may lead to unexpected results.
44 changes: 23 additions & 21 deletions docs/generic-pdo-driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,48 @@
sidebar_position: 10
---

# Generic PDO configuration
# Generic PDO Configuration

If you want to use a PDO driver that is not mapped into the `anydataset-db` library you can use the generic PDO driver.
If you want to use a PDO driver that is not mapped in the `anydataset-db` library, you can use the generic PDO driver.

The generic PDO driver uses the format `pdo://username:password@pdo_driver?PDO_ARGUMENTS`.
The generic PDO driver follows the format:
`pdo://username:password@pdo_driver?PDO_ARGUMENTS`.

That are the steps to get it working:
1. Install the PDO driver properly;
## Steps to Configure Generic PDO

1. Install the PDO driver properly.
2. Adapt the connection string URI to the generic PDO format.
3. Use the `Factory::getDbInstance` to get the database instance.
3. Use `Factory::getDbInstance` to create the database instance.

**IMPORTANT**:
### **IMPORTANT**

Avoid to use Generic PDO Driver if there is a specific `Anydataset` driver for your database.
The specific driver will have more features and better performance.
Whenever possible, use a specific `Anydataset` driver for your database instead of the generic PDO driver. Specific
drivers offer additional features and better performance.

## Adapt the PDO connection string to URI format
## Adapting the PDO Connection String to URI Format

Let's take as example the Firebird PDO driver. The connection string is:
For example, consider the Firebird PDO driver. Its typical connection string may look like this:

```text
firebird:User=john;Password=mypass;Database=DATABASE.GDE;DataSource=localhost;Port=3050
```

and adapting to URI style we remove the information about the driver, user and password. Then we have:
To adapt it to the URI format, remove information about the driver, user, and password, resulting in:

```php
$uri = new Uri("pdo://john:mypass@firebird?Database=DATABASE.GDE&DataSource=localhost&Port=3050");
```

Note the configuration:
### Key Configuration Points:

- The schema for generic PDO is "pdo";
- The host is the PDO driver. In this example is "firebird";
- The PDO arguments are passed as query string. Remember to replace the `;` by `&`.
- The user and password are passed as part of the URI.
- The schema for the generic PDO driver is `"pdo"`.
- The host corresponds to the PDO driver (e.g., `"firebird"`).
- PDO arguments are passed as query parameters in the URI. Replace `;` with `&` to meet URI standards.
- User and password are included as part of the URI.

## Generic rule
## Generic Conversion Rule

From:
Convert:
```text
<pdo-driver>:User=<user>;Password=<password>;[<pdo-arguments>]
```
Expand All @@ -51,9 +53,9 @@ To:
pdo://<user>:<password>@<pdo-driver>?<pdo-arguments>
```

## Using Generic PDO to connect with Unix Socket
## Using Generic PDO to Connect with a Unix Socket

If you want to connect to a MySQL database using Unix Socket you can use the following URI:
To connect to a MySQL database using a Unix Socket, use a URI format like this:

```php
$uri = new Uri("pdo://root:password@mysql?unix_socket=/var/run/mysqld/mysqld.sock&dname=mydatabase");
Expand Down
15 changes: 9 additions & 6 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ sidebar_position: 1

# Getting Started

## 1. Install the ByJG AnyDatasetDB library
## 1. Install the ByJG AnyDatasetDB Library

You can install it via Composer:
Install the library using Composer:

```bash
composer require byjg/anydataset-db
```

## 2. Connect to the database

First, set up a database connection. ByJG AnyDatasetDB supports multiple databases like MySQL, PostgreSQL, SQL Server, and SQLite.
Set up a database connection. ByJG AnyDatasetDB supports multiple databases, including MySQL, PostgreSQL, SQL Server,
Oracle and SQLite.

Here is an example of how to connect to a MySQL database:
Example: Connecting to a MySQL database:

```php
<?php
Expand All @@ -33,7 +34,9 @@ $dbDriver = Factory::createDbDriver($connectionString);

## 3. Perform a query

Once you have your database connection set up, you can perform queries using the DbDriver object. Here's an example of a simple SELECT query:
Once your database connection is established, you can perform queries using the DbDriver object.

Example: Simple SELECT query:

```php
<?php
Expand All @@ -56,7 +59,7 @@ foreach ($iterator as $row) {

## 4. Insert, Update, or Delete data

Here is an example of how to insert, update, or delete data using execute:
You can use the execute method for data manipulation operations.

Insert data:

Expand Down
40 changes: 22 additions & 18 deletions docs/helper.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,32 @@ sidebar_position: 7

# Helper - DbFunctions

AnyDataset has a helper `ByJG\AnyDataset\Db\DbFunctionsInterface` that can be return some specific data based on the database connection.
The AnyDataset library provides a helper interface, `ByJG\AnyDataset\Db\DbFunctionsInterface`, which returns
database-specific SQL operations based on the current database connection.

Methods availables:
## Available Methods

| Method | Description |
|---------------------------------------------------------------------|------------------------------------------------------------------------------------------|
| concat($str1, $str2 = null) | Return the proper concatenation operation based on the current connection |
| limit($sql, $start, $qty) | Return the proper SQL with the LIMIT based on the current connection |
| top($sql, $qty) | Return the proper SQL with the TOP based on the current connection |
| hasTop() | Return true if the current connection has TOP |
| hasLimit() | Return true if the current connection has LIMIT |
| sqlDate($format, $column = null) | Return the proper function to format a date field based on the current connection |
| toDate($date, $dateFormat) | Return the proper function to convert a date to a string based on the current connection |
| fromDate($date, $dateFormat) | Return the proper function to convert a string to a date based on the current connection |
| executeAndGetInsertedId(DbDriverInterface $dbdataset, $sql, $param) | Execute a SQL and return the inserted ID |
| delimiterField($field) | Return the field with proper field delimiter based on the current connection |
| delimiterTable($table) | Return the table with proper table delimiter based on the current connection |
| forUpdate($sql) | Return the SQL with the FOR UPDATE based on the current connection |
| hasForUpdate() | Return true if the current connection has FOR UPDATE |
| Method | Description |
|-----------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| `concat($str1, $str2 = null)` | Returns the proper concatenation operation for the current connection. |
| `limit($sql, $start, $qty)` | Returns the SQL query with the correct `LIMIT` clause for the current connection. |
| `top($sql, $qty)` | Returns the SQL query with the correct `TOP` clause for the current connection. |
| `hasTop()` | Returns `true` if the current connection supports `TOP`. |
| `hasLimit()` | Returns `true` if the current connection supports `LIMIT`. |
| `sqlDate($format, $column = null)` | Returns the proper function to format a date field based on the current connection. |
| `toDate($date, $dateFormat)` | Returns the proper function to convert a date to a string based on the current connection. |
| `fromDate($date, $dateFormat)` | Returns the proper function to convert a string to a date based on the current connection. |
| `executeAndGetInsertedId(DbDriverInterface $dbdataset, $sql, $param)` | Executes a SQL query and returns the inserted ID. |
| `delimiterField($field)` | Returns the field name with the correct field delimiter for the current connection. |
| `delimiterTable($table)` | Returns the table name with the correct table delimiter for the current connection. |
| `forUpdate($sql)` | Returns the SQL query with the `FOR UPDATE` clause for the current connection. |
| `hasForUpdate()` | Returns `true` if the current connection supports `FOR UPDATE`. |

## Use Case

It is useful when you are working with different database connections and don't want to hard code the information there.
The `DbFunctionsInterface` is especially useful when working with multiple database connections. It helps ensure that
SQL operations are dynamically adapted to the specific database being used, avoiding hardcoding database-specific
details in your code.

E.g.

Expand Down
9 changes: 5 additions & 4 deletions docs/iteratorfilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ sidebar_position: 8

# Using IteratorFilter

`IteratorFilter` is a class that helps you to create a filter to be used in the Iterator.
It is standard across all AnyDataset implementations.
`IteratorFilter` is a class that simplifies creating filters for use with an `Iterator`.
It is a standard feature across all AnyDataset implementations.

## Basic Usage

Expand All @@ -31,9 +31,10 @@ $iterator = $db->getIterator($sql, $param);

## Using IteratorFilter with Literal values

Sometimes you need an argument as a Literal value like a function or an explicit conversion.
Sometimes, you may need to use an argument as a literal value, such as a function or an explicit conversion.

In this case you have to create a class that expose the "__toString()" method
In such cases, you need to create a class that implements the `__toString()` method.
This method allows the literal value to be properly represented and used in the filter.

```php
<?php
Expand Down
17 changes: 8 additions & 9 deletions docs/literal-pdo-driver.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
---
sidebar_position: 16
sidebar_position: 17
---

# Literal PDO configuration

If you want to use a PDO driver and this driver is not available in the AnyDatasetDB or and it requires special
parameters don't fit well
using the URI model you can use the literal object.
If you want to use a PDO driver that is not available in AnyDatasetDB or requires special parameters that
do not align well with the URI model, you can use the `PdoLiteral` object.

The `PdoLiteral` object uses the PDO connection string instead of the URI model.
The `PdoLiteral` object allows you to use a traditional PDO connection string instead of the URI model.

Example:

Expand All @@ -18,10 +17,10 @@ Example:
$literal = new \ByJG\AnyDataset\Db\PdoLiteral("sqlite::memory:");
```

Drawbacks:
### Drawbacks

* You can't use the `Factory::getDbInstance` to get the database instance. You need to use the `PdoLiteral` object
directly.
* The DBHelper won't work with the `PdoLiteral` object.
- You cannot use the `Factory::getDbInstance` method to obtain the database instance. Instead, you must use the
`PdoLiteral` object directly.
- The `DBHelper` functionality is not compatible with the `PdoLiteral` object.


Loading
Loading