DB-Object

 view release on metacpan or  search on metacpan

lib/DB/Object/Postgres/Tables.pm  view on Meta::CPAN

    push( @output, "PRIMARY KEY(" . CORE::join( ',', @$primary ) . ")" ) if( $primary && @$primary );
    my $info = $self->stat( $table );
    my @opt  = ();
    my $addons = $info->{create_options};
    if( $addons )
    {
        $addons =~ s/(\A|\s+)([\w\_]+)\s*=\s*/$1\U$2\E=/g;
        push( @opt, $addons );
    }
    push( @opt, "COMMENT='" . quotemeta( $info->{ 'comment' } ) . "'" ) if( $info->{comment} );
    my $str = "CREATE TABLE $table (\n\t" . CORE::join( ",\n\t", @output ) . "\n)";
    $str   .= ' ' . CORE::join( ' ', @opt ) if( @opt );
    $str   .= ';';
    return( @output ? $str : undef() );
}

# NOTE: sub default is inherited from DB::Object::Tables
# sub default

# <https://www.postgresql.org/docs/10/sql-altertable.html>
sub disable_trigger
{
    my $self  = shift( @_ );
    my $table = $self->{table} || 
        return( $self->error( "No table was provided to disable trigger." ) );
    my $opts  = $self->_get_args_as_hash( @_ );
    $opts->{all} //= 0;
    # This feature exists only since version 8.1
    unless( $self->database_object->version >= version->declare( '8.1' ) )
    {
        return( $self->error( "Disabling trigger on a table requires PostgreSQL version 8.1 or higher." ) );
    }
    my $query = 'ALTER TABLE ' . $table . ' DISABLE TRIGGER ';
    if( defined( $opts->{name} ) && length( $opts->{name} ) )
    {
        $query .= $opts->{name};
    }
    else
    {
        $query .= $opts->{all} ? 'ALL' : 'USER';
    }
    my $sth = $self->database_object->prepare( $query ) ||
        return( $self->error( "Error while preparing query to disable trigger for table '$table':\n$query", $self->database_object->errstr() ) );
    if( !defined( wantarray() ) )
    {
        $sth->execute() ||
        return( $self->error( "Error while executing query to disable trigger for table '$table':\n$query", $sth->errstr() ) );
    }
    return( $sth );
}

sub drop
{
    my $self  = shift( @_ );
    my $table = $self->{table} || 
    return( $self->error( "No table was provided to drop." ) );
    my $opts  = $self->_get_args_as_hash( @_ );
    my $query = 'DROP TABLE';
    $query   .= ' IF EXISTS' if( $opts->{ 'if-exists' } || $opts->{if_exists} );
    $query   .= " $table";
    if( $opts->{cascade} )
    {
        $query .= ' CASCADE';
    }
    ## Default Postgres behavior
    elsif( $opts->{restrict} )
    {
        $query .= ' RESTRICT';
    }
    my $sth = $self->database_object->prepare( $query ) ||
    return( $self->error( "Error while preparing query to drop table '$table':\n$query", $self->database_object->errstr() ) );
    if( !defined( wantarray() ) )
    {
        $sth->execute() ||
        return( $self->error( "Error while executing query to drop table '$table':\n$query", $sth->errstr() ) );
    }
    $self->reset_structure;
    return( $sth );
}

# <https://www.postgresql.org/docs/10/sql-altertable.html>
sub enable_trigger
{
    my $self  = shift( @_ );
    my $table = $self->{table} || 
    return( $self->error( "No table was provided to enable trigger." ) );
    my $opts  = $self->_get_args_as_hash( @_ );
    $opts->{all} //= 0;
    # This feature exists only since version 8.1
    unless( $self->database_object->version >= version->declare( '8.1' ) )
    {
        return( $self->error( "Enabling trigger on a table requires PostgreSQL version 8.1 or higher." ) );
    }
    my $query = 'ALTER TABLE ' . $table . ' ENABLE TRIGGER ';
    if( defined( $opts->{name} ) && length( $opts->{name} ) )
    {
        $query .= $opts->{name};
    }
    else
    {
        $query .= $opts->{all} ? 'ALL' : 'USER';
    }
    my $sth = $self->database_object->prepare( $query ) ||
    return( $self->error( "Error while preparing query to disable trigger for table '$table':\n$query", $self->database_object->errstr() ) );
    if( !defined( wantarray() ) )
    {
        $sth->execute() ||
        return( $self->error( "Error while executing query to disable trigger for table '$table':\n$query", $sth->errstr() ) );
    }
    return( $sth );
}

sub exists
{
    my $self = shift( @_ );
    return( $self->table_exists( @_ ? shift( @_ ) : $self->name ) );
}

# NOTE: sub fields is inherited from DB::Object::Tables
# sub fields

lib/DB/Object/Postgres/Tables.pm  view on Meta::CPAN

SELECT 
     n.nspname AS "schema_name"
    ,c.relname AS "table_name"
    ,CASE c.relkind
        WHEN 'r' THEN 'table'
        WHEN 'v' THEN 'view'
        WHEN 'm' THEN 'materialized view'
        WHEN 's' THEN 'special'
        WHEN 'f' THEN 'foreign table'
        WHEN 'p' THEN 'table'
     END as "table_type"
    ,a.attname AS "field"
    ,a.attnum AS "field_num"
    ,CASE
        WHEN a.atttypmod = -1 THEN null
        WHEN t.oid IN (1042, 1043) THEN a.atttypmod - 4
        WHEN t.oid IN (1560, 1562) THEN a.atttypmod
        ELSE NULL
     END AS "character_maximum_length"
    ,CASE SUBSTRING(t.typname,1,1)
        WHEN '_' THEN SUBSTRING(t.typname,2)
        ELSE t.typname
     END AS "data_type"
    ,pg_catalog.format_type(a.atttypid,a.atttypmod) AS "format_type"
    ,a.attndims > 0 AS "is_array"
    ,CASE a.attnotnull
        WHEN FALSE THEN TRUE
        ELSE FALSE
     END AS "is_nullable"
    ,COALESCE(i.indisprimary,false) AS "is_primary"
    ,COALESCE(i.indisunique,false) AS "is_unique"
    ,r.oid IS NOT NULL AS "is_foreign"
    ,r2.oid IS NOT NULL AS "is_check"
    ,(SELECT pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) FROM pg_attrdef 
        WHERE pg_attrdef.adrelid = c.oid 
        AND pg_attrdef.adnum = a.attnum) AS "default"
    ,(SELECT pg_description.description FROM pg_description
        WHERE pg_description.objoid = c.oid AND pg_description.objsubid = a.attnum) AS "comment"
    ,(SELECT c2.relname FROM pg_inherits LEFT JOIN pg_class c2 ON c2.oid = pg_inherits.inhparent
        WHERE pg_inherits.inhrelid = c.oid) AS "table_parent"
    ,c2.relname AS "index_name"
    ,i.indnatts AS "index_n_columns"
    ,i.indkey AS "index_columns"
    ,ARRAY(
        SELECT pg_get_indexdef(i.indexrelid, k, TRUE)
        FROM generate_series(1,array_upper(string_to_array(i.indkey::text, ' ' )::int2[],1)) AS s(k)
        ORDER BY k
    ) AS "index_keys"
    ,r.conname AS "foreign_name"
    ,CASE
        WHEN r.conindid > 0 THEN (SELECT c3.relname FROM pg_catalog.pg_class c3 WHERE c3.oid = r.conindid)
        ELSE NULL
     END AS "foreign_index_name"
    ,CASE
        WHEN r.confrelid > 0 THEN (SELECT c4.relname FROM pg_catalog.pg_class c4 WHERE c4.oid = r.confrelid)
        ELSE NULL
     END AS "foreign_table"
    ,CASE r.confupdtype
        WHEN 'a' THEN 'nothing'
        WHEN 'r' THEN 'restrict'
        WHEN 'c' THEN 'cascade'
        WHEN 'n' THEN 'null'
        WHEN 'd' THEN 'default'
        ELSE NULL
     END AS "foreign_update_action"
    ,CASE r.confdeltype
        WHEN 'a' THEN 'nothing'
        WHEN 'r' THEN 'restrict'
        WHEN 'c' THEN 'cascade'
        WHEN 'n' THEN 'null'
        WHEN 'd' THEN 'default'
        ELSE NULL
     END AS "foreign_delete_action"
    ,CASE r.confmatchtype
        WHEN 'f' THEN 'full'
        WHEN 'p' THEN 'partial'
        WHEN 's' THEN 'simple'
        ELSE NULL
     END AS "foreign_match"
    ,r.conkey AS "foreign_columns"
    ,ARRAY(
        SELECT a2.attname
        FROM generate_series(1,array_upper(r.conkey,1)) AS z(j), pg_catalog.pg_attribute a2
        WHERE a2.attrelid = c.oid AND a2.attnum = r.conkey[j]
    ) AS "foreign_keys"
    ,CASE
        WHEN r.oid IS NOT NULL THEN pg_get_constraintdef(r.oid,TRUE)
        ELSE NULL
     END AS "foreign_expression"
    ,r2.conname AS "check_name"
    ,CASE
        WHEN r2.conindid > 0 THEN (SELECT c3.relname FROM pg_catalog.pg_class c3 WHERE c3.oid = r2.conindid)
        ELSE NULL
     END AS "check_index_name"
    ,r2.conkey AS "check_columns"
    ,ARRAY(
        SELECT a2.attname
        FROM generate_series(1,array_upper(r2.conkey,1)) AS z(j), pg_catalog.pg_attribute a2
        WHERE a2.attrelid = c.oid AND a2.attnum = r2.conkey[j]
    ) AS "check_keys"
    ,CASE
        WHEN r2.oid IS NOT NULL THEN pg_get_constraintdef(r2.oid,TRUE)
        ELSE NULL
     END AS "check_expression"
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
LEFT JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
LEFT JOIN pg_catalog.pg_index i ON i.indrelid = c.oid AND a.attnum = ANY (i.indkey::int[])
LEFT JOIN pg_catalog.pg_class c2 ON c2.oid = i.indexrelid
LEFT JOIN pg_catalog.pg_constraint r ON r.conrelid = c.oid AND r.contype = 'f' AND a.attnum = ANY (r.conkey)
LEFT JOIN pg_catalog.pg_constraint r2 ON r2.conrelid = c.oid AND r2.contype = 'c' AND a.attnum = ANY (r2.conkey)
WHERE
    c.relname = ? AND
    a.attnum > 0 AND
    NOT a.attisdropped
ORDER BY a.attnum
EOT
    # http://www.postgresql.org/docs/9.3/interactive/infoschema-columns.html
    # select * from information_schema.columns where table_name = 'address'
    my $sth = $self->database_object->prepare_cached( $query ) ||
        return( $self->error( "Error while preparing query to get table '$table' columns specification: ", $self->database_object->errstr ) );
    $sth->execute( $table ) ||
        return( $self->error( "Error while executing query to get table '$table' columns specification: ", $sth->errstr ) );
    my $check = {};
    my $foreign = {};
    my $index = {};
    my @primary = ();
    my $ref = '';

lib/DB/Object/Postgres/Tables.pm  view on Meta::CPAN

If provided, this will create a temporary table.

=item * C<with oids>

If true, this will enable table oid

=item * C<without oids>

If true, this will disable table oid

=back

This will return an error if the table already exists, so best to check beforehand with L</exists>.

Upon success, it will return the new statement to create the table. However, if L</create> is called in void context, then the statement is executed right away and returned.

=head2 create_info

This returns the create info for the current table object as a string representing the sql script necessary to recreate the table.

=head2 disable_trigger

    my $sth = $tbl->disable_trigger;
    my $sth = $tbl->disable_trigger( all => 1 );
    my $sth = $tbl->disable_trigger( name => 'my_trigger' );

Provided with some optional parameters and this will return a statement handler to disable all triggers or a given trigger on the table.

If it is called in void context, then the statement is executed immediately and returned, otherwise it is just returned.

    $tbl->disable_trigger;
    # would issue immediately the following query:
    ALTER TABLE my_table DISABLE TRIGGER USER

It takes the following options:

=over 4

=item * C<all>

If true, this will disable all trigger on the table. Please note that, as per the L<PostgreSQL documentation|https://www.postgresql.org/docs/10/sql-altertable.html> this requires super user privilege.

If false, this will disable only the user triggers, i.e. not including the system ones.

=item * C<name>

If a trigger name is provided, it will be used to specifically disable this trigger.

=back

=head2 drop

This will prepare a drop statement to drop the current table.

If it is called in void context, then the statement is executed immediately and returned, otherwise it is just returned.

It takes the following options:

=over 4

=item * C<cascade>

If true, C<CASCADE> will be added to the C<DROP> query.

=item * C<if_exists>

If true, this will add a C<IF EXISTS> to the C<DROP> query.

You can also use I<if-exists>

=item * C<restrict>

If true, C<RESTRICT> will be added to the C<DROP> query.

=back

See L<PostgreSQL documentation for more information|https://www.postgresql.org/docs/9.5/sql-droptable.html>

=head2 enable_trigger

    my $sth = $tbl->enable_trigger;
    my $sth = $tbl->enable_trigger( all => 1 );
    my $sth = $tbl->enable_trigger( name => 'my_trigger' );

Provided with some optional parameters and this will return a statement handler to enable all triggers or a given trigger on the table.

If it is called in void context, then the statement is executed immediately and returned, otherwise it is just returned.

    $tbl->enable_trigger;
    # would issue immediately the following query:
    ALTER TABLE my_table ENABLE TRIGGER USER

It takes the following options:

=over 4

=item * C<all>

If true, this will enable all trigger on the table. Please note that, as per the L<PostgreSQL documentation|https://www.postgresql.org/docs/10/sql-altertable.html> this requires super user privilege.

If false, this will enable only the user triggers, i.e. not including the system ones.

=item * C<name>

If a trigger name is provided, it will be used to specifically enable this trigger.

=back

=head2 exists

Returns true if the current table exists, or false otherwise.

=head2 foreign

Sets or gets an hash reference of foreign key constraint name to an hash of properties for that constraint.

Available properties are:

=over 4

=item * C<expr>

The foreign key expression used when creating the table schema.

=item * C<match>

Typical value is C<full>, C<partial> and C<simple>

=item * C<on_delete>

The action the database is to take upon deletion. For example: C<nothing>, C<restrict>, C<cascade>, C<null> or C<default>

=item * C<on_update>

The action the database is to take upon update. For example: C<nothing>, C<restrict>, C<cascade>, C<null> or C<default>

=item * C<table>

The table name of the foreign key.

=item * C<fields>

The associated table column names for this foreign key constraint.

=item * C<name>

The foreign key constraint name.

=back

=head2 lock

This will prepare a query to lock the table and return the statement handler. If it is called in void context, the statement handler returned is executed immediately.

It takes an optional lock type and an optional C<NOWAIT> parameter.

Supported lock types are:

=over 4

=item C<ACCESS SHARE>

=item C<ROW SHARE>

=item C<ROW EXCLUSIVE>

=item C<SHARE UPDATE EXCLUSIVE>

=item C<SHARE>

=item C<SHARE ROW EXCLUSIVE>

=item C<EXCLUSIVE>

=item C<ACCESS EXCLUSIVE>

=back

See L<PostgreSQL documentation for more information|https://www.postgresql.org/docs/9.5/explicit-locking.html>

=head2 on_conflict

A convenient wrapper to L<DB::Object::Postgres::Query/on_conflict>

=head2 optimize

Not implemented in PostgreSQL.

=head2 parent

This will return the parent table if the current table inherits from another table.

=head2 qualified_name

This return a fully qualified name to be used as a prefix to columns in queries.



( run in 0.657 second using v1.01-cache-2.11-cpan-39bf76dae61 )