Giter VIP home page Giter VIP logo

oapth's Introduction

Oapth

CI crates.io Documentation License Rustc

Oapth is a suite of tools that interact with databases.

Schema Manager

Using SQL migrations, supports embedded and CLI workflows for MS-SQL, MySQL, PostgreSQL and SQLite.

This project tries to support all database bridges of the Rust ecosystem, is fully documented, applies fuzz tests in some targets and doesn't make use of expect, indexing, panic, unsafe or unwrap.

CLI

# Example

cargo install oapth-cli --features sm-dev,postgres --git https://github.com/c410-f3r/oapth
echo DATABASE_URL="postgres://USER:PW@localhost:5432/DB" > .env
RUST_LOG=debug oapth-cli migrate

The CLI application expects a configuration file that contains a set of paths where each path is a directory with multiple migrations.

# oapth.toml

migration_groups = [
  "migrations/1__initial",
  "migrations/2__fancy_stuff"
]

Each provided migration and group must contain an unique version and a name summarized by the following structure:

// Execution order of migrations is dictated by their numeric declaration order.

migrations
+-- 1__initial (Group)
    +-- 1__create_author.sql (Migration)
    +-- 2__create_post.sql (Migration)
+-- 2__fancy_stuff (Group)
    +-- 1__something_fancy.sql (Migration)
oapth.toml

The SQL file itself is composed by two parts, one for migrations (-- oapth UP section) and another for rollbacks (-- oapth DOWN section).

-- oapth UP

CREATE TABLE author (
  id INT NOT NULL PRIMARY KEY,
  added TIMESTAMP NOT NULL,
  birthdate DATE NOT NULL,
  email VARCHAR(100) NOT NULL,
  first_name VARCHAR(50) NOT NULL,
  last_name VARCHAR(50) NOT NULL
);

-- oapth DOWN

DROP TABLE author;

One cool thing about the expected file configuration is that it can also be divided into smaller pieces, for example, the above migration could be transformed into 1__author_up.sql and 1__author_down.sql.

-- 1__author_up.sql

CREATE TABLE author (
  id INT NOT NULL PRIMARY KEY,
  added TIMESTAMP NOT NULL,
  birthdate DATE NOT NULL,
  email VARCHAR(100) NOT NULL,
  first_name VARCHAR(50) NOT NULL,
  last_name VARCHAR(50) NOT NULL
);
-- 1__author_down.sql

DROP TABLE author;
migrations
+-- 1__some_group (Group)
    +-- 1__author (Migration directory)
        +-- 1__author_down.sql (Down migration)
        +-- 1__author_up.sql (Up migration)
        +-- 1__author.toml (Optional configuration)
oapth.toml

Library

The library gives freedom to arrange groups and uses some external crates, bringing ~10 additional dependencies into your application. If this overhead is not acceptable, then you probably should discard the library and use the CLI binary instead as part of a custom deployment strategy.

use oapth::{Commands, Config, SqlxPg};
use std::path::Path;

#[tokio::main]
async fn main() -> oapth::Result<()> {
  let config = Config::with_url_from_default_var()?;
  let mut commands = Commands::with_backend(SqlxPg::new(&config).await?);
  commands.migrate_from_dir(Path::new("my_custom_migration_group_path"), 128).await?;
  Ok(())
}

One thing worth noting is that these mandatory dependencies might already be part of your application as transients. In case of doubt, check your Cargo.lock file or type cargo tree for analysis.

Embedded migrations

To make deployment easier, the final binary of your application can embed all necessary migrations by using the embed_migrations! macro that is available when selecting the embed-migrations feature.

use oapth::{Commands, Config, EmbeddedMigrationsTy, MysqlAsync, embed_migrations};

const MIGRATIONS: EmbeddedMigrationsTy = embed_migrations!("SOME_CONFIGURATION_FILE.toml");

#[tokio::main]
async fn main() -> oapth::Result<()> {
  let config = Config::with_url_from_default_var()?;
  let mut commands = Commands::with_backend(MysqlAsync::new(&config).await?);
  let groups = MIGRATIONS.iter().map(|e| (e.0, e.1.iter().cloned()));
  commands.migrate_from_groups(groups).await?;
  Ok(())
}

Conditional migrations

If one particular migration needs to be executed in a specific set of databases, then it is possible to use the -- oapth dbs parameter in a file.

-- oapth dbs mssql,postgres

-- oapth UP

CREATE SCHEMA foo;

-- oapth DOWN

DROP SCHEMA foo;

Repeatable migrations

Repeatability can be specified with -- oapth repeatability SOME_VALUE where SOME_VALUE can be either always (regardless of the checksum) or on-checksum-change (runs only when the checksums changes).

-- oapth dbs postgres
-- oapth repeatability always

-- oapth UP

CREATE OR REPLACE PROCEDURE something() LANGUAGE SQL AS $$ $$

-- oapth DOWN

DROP PROCEDURE something();

Keep in mind that repeatable migrations might break subsequent operations, therefore, you must known what you are doing. If desirable, they can be separated into dedicated groups.

migrations/1__initial_repeatable_migrations
migrations/2__normal_migrations
migrations/3__final_repeatable_migrations

Namespaces/Schemas

For supported databases, there is no direct user parameter that inserts migrations inside a single database schema but it is possible to specify the schema inside the SQL file and arrange the migration groups structure in a way that most suits you.

-- oapth UP

CREATE TABLE cool_department_schema.author (
  id INT NOT NULL PRIMARY KEY,
  full_name VARCHAR(50) NOT NULL
);

-- oapth DOWN

DROP TABLE cool_department_schema.author;

ORM

Currently only contains very basic support for CRUD operations.

oapth's People

Contributors

c410-f3r avatar zacck avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

undef1nd zacck-zz

oapth's Issues

Still alive?

Hi there!
I was just wondering if the project is still alive and what is missing to do a release on crates.
I am looking to switch my current diesel migrations to something more universal and your library seems very well made.
Thanks

Meta issue for `clean` method

Description

This issue gathers all remaining missing aspects of clean and also serves as a general tutorial.

The clean method tries to clean all objects of a database, including user created schemas, but is very tricky to implement for all databases. To make life easier, the current implementation of flyway is going to be used as a development reference (hope the flyway guys don't mind).

1. Choose a database and a missing target

For example, let's pick PostgreSQL to see what flyway does and what oapth currently can do:

https://github.com/flyway/flyway/blob/e2ffad5790f484b434a761f23e431ba8e3e232f8/flyway-core/src/main/java/org/flywaydb/core/internal/database/postgresql/PostgreSQLSchema.java#L81

pub async fn clean<B>(back_end: &mut B) -> crate::Result<ArrayString<[u8; 2048]>>

By looking into both projects, it is possible to see that oapth doesn't remove sequences, so let's implement it.

2. Implementation

Let's create the function that lists all sequences (don't forget to link and credit the original flyway source):

// https://github.com/flyway/flyway/blob/master/flyway-core/src/main/java/org/flywaydb/core/internal/database/postgresql/PostgreSQLSchema.java
#[oapth_macros::dev_tools_]
#[inline]
pub async fn sequences<B>(back_end: &mut B, schema: &str) -> crate::Result<Vec<String>>
where
  B: crate::BackEnd
{
  let mut buffer = ArrayString::<[u8; 128]>::new();
  buffer.write_fmt(format_args!(
    "SELECT sequence_name AS generic_column FROM information_schema.sequences WHERE sequence_schema = '{schema}'",
    schema = schema
  ))?;
  Ok(back_end.query_string(&buffer).await?)
}

And insert the drop statement within the clean function:

for sequence in sequences(back_end, "public").await? {
  buffer.write_fmt(format_args!("DROP SEQUENCE {};", domain))?;
}

3. Testing

Since this is a specific behavior of PostgreSQL, we are going to include some code into the already existing clean_drops_all_objs test:

pub async fn clean_drops_all_objs<B>(

...

c.back_end.execute("CREATE SEQUENCE serial START 101;").await.unwrap();

...

assert_eq!(crate::fixed_sql_commands::pg::sequences(&mut c.back_end).await.unwrap().len(), 1);

...

c.clean().await.unwrap();

...

assert_eq!(crate::fixed_sql_commands::pg::sequences(&mut c.back_end).await.unwrap().len(), 0);

...

Now it is only a matter of running the test. Go to the project root directory and type ./scripts/integration-tests-pg.sh to run your shinny new test through the diesel, tokio_postgres and sqlx database bridges.

Check out https://github.com/c410-f3r/oapth/blob/master/CONTRIBUTING.md for more information.

Issues

MS-SQL

MySql

PostgreSQL

SQLite

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.