[Bps-public-commit] dbix-searchbuilder branch, master, updated. 1.67-11-g9390ab2

? sunnavy sunnavy at bestpractical.com
Wed Jan 20 15:16:18 EST 2021


The branch, master has been updated
       via  9390ab2468cc5ea3d2b52414e5d9e2aae07730d2 (commit)
       via  2b487b024b86b40c30eb3e5db977c96bc8ab5bff (commit)
       via  b053f35c463315b3b96e3d18ddd2b581d5a18892 (commit)
      from  5a6aa796446e627fb0f70e1ef6fbde553d97e34d (commit)

Summary of changes:
 Changes                                |  5 ++
 MANIFEST                               |  1 +
 META.yml                               |  2 +-
 lib/DBIx/SearchBuilder.pm              |  3 +-
 lib/DBIx/SearchBuilder/Handle.pm       | 83 ++++++++++++++++++++++++++++++++--
 lib/DBIx/SearchBuilder/Handle/mysql.pm | 60 ++++++++++++++++++++++++
 lib/DBIx/SearchBuilder/Record.pm       | 24 ++++++++--
 t/02searches_function.t                |  2 +-
 t/02searches_joins.t                   |  2 +-
 t/03cud_from_select.t                  |  7 +--
 t/04mysql_identifier_quoting.t         | 17 +++++++
 t/11schema_records.t                   | 59 ++++++++++++++++++++++--
 12 files changed, 247 insertions(+), 18 deletions(-)
 create mode 100644 t/04mysql_identifier_quoting.t

- Log -----------------------------------------------------------------
commit b053f35c463315b3b96e3d18ddd2b581d5a18892
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Tue Aug 25 21:16:11 2020 +0100

    Add support for automatic quoting of table names
    
    MySQL 8 adds new reserved and keywords, that will cause queries with tables or
    columns of those names to fail with syntax errors.
    https://dev.mysql.com/doc/mysqld-version-reference/en/keywords-8-0.html
    
    The new QuoteTableNames option will automatically quote tables in SQL created
    by SearchBuilder, and will enabled if MySQL version 8 is detected. New helpers
    simplify quoting and de-quoting table or column names.

diff --git a/Changes b/Changes
index bed91f1..35316a7 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,8 @@
 Revision history for Perl extension DBIx::SearchBuilder.
 
+ - New option to quote tablenames in queries, enabled automatically for MySQL 8
+ - Updated tests for new MySQL 8 reserved words and tablename quoting
+
 1.68 2020-07-06
  - Avoid segmentation faults on disconnect on MariaDB 10.2+
 
diff --git a/lib/DBIx/SearchBuilder.pm b/lib/DBIx/SearchBuilder.pm
index 195669e..e9b9326 100755
--- a/lib/DBIx/SearchBuilder.pm
+++ b/lib/DBIx/SearchBuilder.pm
@@ -1251,6 +1251,7 @@ sub NewAlias {
 
     my $alias = $self->_GetAlias($table);
 
+    $table = $self->_Handle->QuoteName($table) if $self->_Handle->QuoteTableNames;
     unless ( $type ) {
         push @{ $self->{'aliases'} }, "$table $alias";
     } elsif ( lc $type eq 'left' ) {
diff --git a/lib/DBIx/SearchBuilder/Handle.pm b/lib/DBIx/SearchBuilder/Handle.pm
index d0f67fc..ed49ff7 100755
--- a/lib/DBIx/SearchBuilder/Handle.pm
+++ b/lib/DBIx/SearchBuilder/Handle.pm
@@ -50,13 +50,17 @@ sub new  {
     my $self  = {};
     bless ($self, $class);
 
+    # Enable quotes table names
+    my %args = ( QuoteTableNames => 0, @_ );
+    $self->{'QuoteTableNames'} = $args{QuoteTableNames};
+
     @{$self->{'StatementLog'}} = ();
     return $self;
 }
 
 
 
-=head2 Connect PARAMHASH: Driver, Database, Host, User, Password
+=head2 Connect PARAMHASH: Driver, Database, Host, User, Password, QuoteTableNames
 
 Takes a paramhash and connects to your DBI datasource. 
 
@@ -71,6 +75,9 @@ If you created the handle with
 and there is a DBIx::SearchBuilder::Handle::(Driver) subclass for the driver you have chosen,
 the handle will be automatically "upgraded" into that subclass.
 
+QuoteTableNames option will force all table names to be quoted if the driver subclass has a method
+for quoting implemented. The mysql subclass will detect mysql version 8 and set the flag.
+
 =cut
 
 sub Connect  {
@@ -85,6 +92,7 @@ sub Connect  {
         Password => undef,
         RequireSSL => undef,
         DisconnectHandleOnDestroy => undef,
+        QuoteTableNames => undef,
         @_
     );
 
@@ -96,6 +104,9 @@ sub Connect  {
     # So we need to explicitly call it
     $self->{'DisconnectHandleOnDestroy'} = $args{'DisconnectHandleOnDestroy'};
 
+    # Enable optional quoted table names
+    $self->{'QuoteTableNames'} = delete $args{QuoteTableNames} if defined $args{QuoteTableNames};
+
     my $old_dsn = $self->DSN || '';
     my $new_dsn = $self->BuildDSN( %args );
 
@@ -116,6 +127,9 @@ sub Connect  {
     # Cache version info
     $self->DatabaseVersion;
 
+    # force quoted tables for mysql 8
+    $self->{'QuoteTableNames'} = 1 if $self->_RequireQuotedTables;
+
     return 1;
 }
 
@@ -390,6 +404,7 @@ sub InsertQueryString {
         push @bind, shift @pairs;
     }
 
+    $table = $self->QuoteName($table) if $self->QuoteTableNames;
     my $QueryString = "INSERT INTO $table";
     $QueryString .= " (". join(", ", @cols) .")";
     $QueryString .= " VALUES (". join(", ", @vals). ")";
@@ -415,6 +430,7 @@ sub InsertFromSelect {
     $columns = join ', ', @$columns
         if $columns;
 
+    $table = $self->QuoteName($table) if $self->{'QuoteTableNames'};
     my $full_query = "INSERT INTO $table";
     $full_query .= " ($columns)" if $columns;
     $full_query .= ' '. $query;
@@ -446,6 +462,7 @@ sub UpdateRecordValue {
                  @_ );
 
     my @bind  = ();
+    $args{Table} = $self->QuoteName($args{Table}) if $self->{'QuoteTableNames'};
     my $query = 'UPDATE ' . $args{'Table'} . ' ';
      $query .= 'SET '    . $args{'Column'} . '=';
 
@@ -518,6 +535,7 @@ sub SimpleUpdateFromSelect {
         push @binds, $values->{$k};
     }
 
+    $table = $self->QuoteName($table) if $self->{'QuoteTableNames'};
     my $full_query = "UPDATE $table SET ";
     $full_query .= join ', ', map "$_ = ?", @columns;
     $full_query .= ' WHERE id IN ('. $query .')';
@@ -541,6 +559,7 @@ select query, eg:
 
 sub DeleteFromSelect {
     my ($self, $table, $query, @binds) = @_;
+    $table = $self->QuoteName($table) if $self->{'QuoteTableNames'};
     my $sth = $self->SimpleQuery(
         "DELETE FROM $table WHERE id IN ($query)",
         @binds
@@ -753,6 +772,16 @@ sub CaseSensitive {
     return(1);
 }
 
+=head2 QuoteTableNames
+
+Returns 1 if table names will be quoted in queries, otherwise 0
+
+=cut
+
+sub QuoteTableNames  {
+    return shift->{'QuoteTableNames'}
+}
+
 
 
 
@@ -1016,6 +1045,7 @@ sub Join {
             if ( $old_alias =~ /^(.*?) (\Q$args{'ALIAS2'}\E)$/ ) {
                 $args{'TABLE2'} = $1;
                 $alias = $2;
+                $args{'TABLE2'} = $self->DequoteName($args{'TABLE2'}) if $self->QuoteTableNames;
             }
             else {
                 push @new_aliases, $old_alias;
@@ -1039,7 +1069,7 @@ sub Join {
                 if ( $old_alias =~ /^(.*?) ($args{'ALIAS2'})$/ ) {
                     $args{'TABLE2'} = $1;
                     $alias = $2;
-
+                    $args{'TABLE2'} = $self->DequoteName($args{'TABLE2'}) if $self->QuoteTableNames;
                 }
                 else {
                     push @new_aliases, $old_alias;
@@ -1088,6 +1118,7 @@ sub Join {
     } else {
         $alias = $args{'SearchBuilder'}->_GetAlias( $args{'TABLE2'} );
     }
+    $args{TABLE2} = $self->QuoteName($args{TABLE2}) if $self->QuoteTableNames;
 
     my $meta = $args{'SearchBuilder'}->{'left_joins'}{"$alias"} ||= {};
     if ( $args{'TYPE'} =~ /LEFT/i ) {
@@ -1161,6 +1192,7 @@ sub _NormalJoin {
     if ( $args{'TYPE'} =~ /LEFT/i ) {
         my $alias = $sb->_GetAlias( $args{'TABLE2'} );
         my $meta = $sb->{'left_joins'}{"$alias"} ||= {};
+        $args{TABLE2} = $self->QuoteName($args{TABLE2}) if $self->QuoteTableNames;
         $meta->{'alias_string'} = " LEFT JOIN $args{'TABLE2'} $alias ";
         $meta->{'depends_on'}   = $args{'ALIAS1'};
         $meta->{'type'}         = 'LEFT';
@@ -1192,8 +1224,9 @@ sub _BuildJoins {
     my $sb   = shift;
 
     $self->OptimizeJoins( SearchBuilder => $sb );
+    my $table = $self->{'QuoteTableNames'} ? $self->QuoteName($sb->Table) : $sb->Table;
 
-    my $join_clause = join " CROSS JOIN ", ($sb->Table ." main"), @{ $sb->{'aliases'} };
+    my $join_clause = join " CROSS JOIN ", ("$table main"), @{ $sb->{'aliases'} };
     my %processed = map { /^\S+\s+(\S+)$/; $1 => 1 } @{ $sb->{'aliases'} };
     $processed{'main'} = 1;
 
@@ -1738,6 +1771,50 @@ sub HasSupportForNullsOrder {
 }
 
 
+=head2 QuoteName
+
+Quote table or column name to avoid reserved word errors.
+
+Returns same value passed unless over-ridden in database-specific subclass.
+
+=cut
+
+# over-ride in subclass
+sub QuoteName {
+    my ($self, $name) = @_;
+    # use dbi built in quoting if we have a connection,
+    if ($self->dbh) {
+        return $self->dbh->quote_identifier($name);
+    }
+    warn "QuoteName called without a db handle";
+    return $name;
+}
+
+=head2 DequoteName
+
+Undo the effects of QuoteName by removing quoting.
+
+=cut
+
+sub DequoteName {
+    my ($self, $name) = @_;
+    if ($self->dbh) {
+        # 29 = SQL_IDENTIFIER_QUOTE_CHAR; see "perldoc DBI"
+        my $quote_char = $self->dbh->get_info( 29 );
+
+        if ($quote_char) {
+            if ($name =~ /^$quote_char(.*)$quote_char$/) {
+                return $1;
+            }
+        }
+        return $name;
+    }
+    warn "DequoteName called without a db handle";
+    return $name;
+}
+
+sub _RequireQuotedTables { return 0 };
+
 =head2 DESTROY
 
 When we get rid of the Searchbuilder::Handle, we need to disconnect from the database
diff --git a/lib/DBIx/SearchBuilder/Handle/mysql.pm b/lib/DBIx/SearchBuilder/Handle/mysql.pm
index a0302aa..0e24364 100755
--- a/lib/DBIx/SearchBuilder/Handle/mysql.pm
+++ b/lib/DBIx/SearchBuilder/Handle/mysql.pm
@@ -74,6 +74,7 @@ sub SimpleUpdateFromSelect {
         push @binds, $values->{$k};
     }
 
+    $table = $self->QuoteName($table) if $self->{'QuoteTableNames'};
     my $update_query = "UPDATE $table SET "
         . join( ', ', map "$_ = ?", @columns )
         .' WHERE ID IN ';
@@ -92,6 +93,7 @@ sub DeleteFromSelect {
         $table, $query, @query_binds
     ) unless $query =~ /\b\Q$table\E\b/i;
 
+    $table = $self->QuoteName($table) if $self->{'QuoteTableNames'};
     return $self->SimpleMassChangeFromSelect(
         "DELETE FROM $table WHERE id IN ", [],
         $query, @query_binds
@@ -289,6 +291,64 @@ sub _DateTimeIntervalFunction {
     return "TIMESTAMPDIFF(SECOND, $args{'From'}, $args{'To'})";
 }
 
+
+=head2 QuoteName
+
+Quote table or column name to avoid reserved word errors.
+
+=cut
+
+# over-rides inherited method
+sub QuoteName {
+    my ($self, $name) = @_;
+    # use dbi built in quoting if we have a connection,
+    if ($self->dbh) {
+        return $self->SUPER::QuoteName($name);
+    }
+
+    return sprintf('`%s`', $name);
+}
+
+sub DequoteName {
+    my ($self, $name) = @_;
+
+    # If we have a handle, the base class can do it for us
+    if ($self->dbh) {
+        return $self->SUPER::DequoteName($name);
+    }
+
+    if ($name =~ /^`(.*)`$/) {
+        return $1;
+    }
+    return $name;
+}
+
+sub _IsMariaDB {
+    my $self = shift;
+
+    # We override DatabaseVersion to chop off "-MariaDB-whatever", so
+    # call super here to get the original version
+    my $v = $self->SUPER::DatabaseVersion();
+
+    return ($v =~ /mariadb/i);
+}
+
+sub _RequireQuotedTables {
+    my $self = shift;
+
+    # MariaDB version does not match mysql, and hasn't added new reserved words
+    return 0 if $self->_IsMariaDB;
+
+    my $version = $self->DatabaseVersion;
+
+    # Get major version number by chopping off everything after the first "."
+    $version =~ s/\..*//;
+    if ( $version >= 8 ) {
+        return 1;
+    }
+    return 0;
+}
+
 1;
 
 __END__
diff --git a/lib/DBIx/SearchBuilder/Record.pm b/lib/DBIx/SearchBuilder/Record.pm
index c1428d0..d5e6f2f 100755
--- a/lib/DBIx/SearchBuilder/Record.pm
+++ b/lib/DBIx/SearchBuilder/Record.pm
@@ -709,7 +709,7 @@ sub __Value {
     my %pk = $self->PrimaryKeys;
     return undef if grep !defined, values %pk;
 
-    my $query = "SELECT $field FROM ". $self->Table
+    my $query = "SELECT $field FROM ". $self->QuotedTableName
         ." WHERE ". join " AND ", map "$_ = ?", sort keys %pk;
     my $sth = $self->_Handle->SimpleQuery( $query, sorted_values(%pk) ) or return undef;
     return $self->{'values'}{$field} = ($sth->fetchrow_array)[0];
@@ -1147,8 +1147,8 @@ sub LoadByCols  {
 
 	}
     }
-    
-    my $QueryString = "SELECT  * FROM ".$self->Table." WHERE ". 
+
+    my $QueryString = "SELECT  * FROM ".$self->QuotedTableName." WHERE ".
     join(' AND ', @phrases) ;
     return ($self->_LoadFromSQL($QueryString, @bind));
 }
@@ -1349,7 +1349,7 @@ sub __Delete {
     }
 
     $where =~ s/AND\s$//;
-    my $QueryString = "DELETE FROM ". $self->Table . ' ' . $where;
+    my $QueryString = "DELETE FROM ". $self->QuotedTableName . ' ' . $where;
    my $return = $self->_Handle->SimpleQuery($QueryString, @bind);
 
     if (UNIVERSAL::isa($return, 'Class::ReturnValue')) {
@@ -1379,7 +1379,23 @@ sub Table {
     return ($self->{'table'});
 }
 
+=head2 QuotedTableName
 
+Returns the name of current Table, or the table provided as an argument, including any quoting
+ based on yje Handle's QuoteTableNames flag and driver method.
+
+=cut
+
+sub QuotedTableName {
+    my ($self, $name) = @_;
+    unless ($name) {
+        return $self->{'_quoted_table'} if defined $self->{'_quoted_table'};
+        $self->{'_quoted_table'}
+            = $self->_Handle->QuoteTableNames ? $self->_Handle->QuoteName( $self->Table ) : $self->Table;
+        return $self->{'_quoted_table'};
+    }
+    return $self->_Handle->QuoteTableNames ? $self->_Handle->QuoteName($name) : $name;
+}
 
 =head2 _Handle
 
diff --git a/t/02searches_function.t b/t/02searches_function.t
index 2fd6e7e..51069ed 100644
--- a/t/02searches_function.t
+++ b/t/02searches_function.t
@@ -168,7 +168,7 @@ CREATE TEMPORARY TABLE UsersToGroups (
     GroupId integer
 ) },
 q{
-CREATE TEMPORARY TABLE Groups (
+CREATE TEMPORARY TABLE `Groups` (
     id integer primary key AUTO_INCREMENT,
     Name varchar(36)
 ) },
diff --git a/t/02searches_joins.t b/t/02searches_joins.t
index 1661f2b..916c42c 100644
--- a/t/02searches_joins.t
+++ b/t/02searches_joins.t
@@ -336,7 +336,7 @@ CREATE TEMPORARY TABLE UsersToGroups (
     GroupId integer
 ) },
 q{
-CREATE TEMPORARY TABLE Groups (
+CREATE TEMPORARY TABLE `Groups` (
     id integer primary key AUTO_INCREMENT,
     Name varchar(36)
 ) },
diff --git a/t/03cud_from_select.t b/t/03cud_from_select.t
index 7f5d21b..7c7974a 100644
--- a/t/03cud_from_select.t
+++ b/t/03cud_from_select.t
@@ -20,6 +20,7 @@ SKIP: {
         skip "ENV is not defined for driver '$d'", TESTS_PER_DRIVER;
     }
 
+    my $groups_table = ($d eq 'mysql') ? '`Groups`' : 'Groups';
     my $handle = get_handle( $d );
     connect_handle( $handle );
     isa_ok($handle->dbh, 'DBI::db');
@@ -50,7 +51,7 @@ diag "insert into table from two tables" if $ENV{'TEST_VERBOSE'};
 {
     my $res = $handle->InsertFromSelect(
         'UsersToGroups' => ['UserId', 'GroupId'],
-        'SELECT u.id as col1, g.id as col2 FROM Users u, Groups g WHERE u.Login LIKE ? AND g.Name = ?',
+        "SELECT u.id as col1, g.id as col2 FROM Users u, $groups_table g WHERE u.Login LIKE ? AND g.Name = ?",
         '%a%', 'Support'
     );
     is( $res, 2 );
@@ -113,7 +114,7 @@ diag "insert into table from two tables" if $ENV{'TEST_VERBOSE'};
         local $SIG{__WARN__} = sub {};
         $handle->InsertFromSelect(
             'UsersToGroups' => ['UserId', 'GroupId'],
-            'SELECT u.id, g.id FROM Users u, Groups g WHERE u.Login LIKE ? AND g.Name = ?',
+            "SELECT u.id, g.id FROM Users u, $groups_table g WHERE u.Login LIKE ? AND g.Name = ?",
             '%a%', 'Support'
         );
     };
@@ -172,7 +173,7 @@ CREATE TABLE UsersToGroups (
     GroupId integer
 ) },
 q{
-CREATE TEMPORARY TABLE Groups (
+CREATE TEMPORARY TABLE `Groups` (
     id integer primary key AUTO_INCREMENT,
     Name varchar(36)
 ) },
diff --git a/t/04mysql_identifier_quoting.t b/t/04mysql_identifier_quoting.t
new file mode 100644
index 0000000..a734f3f
--- /dev/null
+++ b/t/04mysql_identifier_quoting.t
@@ -0,0 +1,17 @@
+#!/usr/bin/perl -w
+
+
+use strict;
+use warnings;
+use Test::More tests => 7;
+
+BEGIN { use_ok("DBIx::SearchBuilder::Handle"); }
+BEGIN { use_ok("DBIx::SearchBuilder::Handle::mysql"); }
+
+my $h = DBIx::SearchBuilder::Handle::mysql->new();
+
+is ($h->QuoteName('foo'), '`foo`', 'QuoteName works as expected');
+is ($h->DequoteName('`foo`'), 'foo', 'DequoteName works as expected');
+is ($h->DequoteName('`foo'), '`foo', 'DequoteName works as expected');
+is ($h->DequoteName('foo`'), 'foo`', 'DequoteName works as expected');
+is ($h->DequoteName('"foo"'), '"foo"', 'DequoteName works as expected');
diff --git a/t/11schema_records.t b/t/11schema_records.t
index 4fc4dc4..7697a2c 100644
--- a/t/11schema_records.t
+++ b/t/11schema_records.t
@@ -8,7 +8,7 @@ use Test::More;
 BEGIN { require "t/utils.pl" }
 our (@AvailableDrivers);
 
-use constant TESTS_PER_DRIVER => 63;
+use constant TESTS_PER_DRIVER => 66;
 
 my $total = scalar(@AvailableDrivers) * TESTS_PER_DRIVER;
 plan tests => $total;
@@ -180,7 +180,13 @@ SKIP: {
 	    is($phone_collection->Next, undef);
 	}
 	
-	
+    ok( $phone3->Delete, "Deleted phone $p3_id" );
+
+    my $group = TestApp::Group->new($handle);
+    my $g_id  = $group->Create( Name => 'Employees' );
+    ok( $g_id, "Got an id for the new group: $g_id" );
+    $group->Load($g_id);
+    is( $group->id, $g_id, "loaded group ok" );
 
 	cleanup_schema( 'TestApp', $handle );
 }} # SKIP, foreach blocks
@@ -201,6 +207,10 @@ CREATE TABLE Phones (
 	id integer primary key,
 	Employee integer NOT NULL,
 	Phone varchar(18)
+) },
+q{CREATE TABLE Groups (
+	id integer primary key,
+	Name varchar(36)
 ) }
 ]
 }
@@ -217,7 +227,12 @@ CREATE TEMPORARY TABLE Phones (
 	Employee integer NOT NULL,
 	Phone varchar(18)
 )
-} ]
+},
+q{CREATE TEMPORARY TABLE `Groups` (
+	id integer AUTO_INCREMENT primary key,
+	Name varchar(36)
+) }
+]
 }
 
 sub schema_pg {
@@ -232,7 +247,12 @@ CREATE TEMPORARY TABLE Phones (
 	Employee integer references Employees(id),
 	Phone varchar
 )
-} ]
+},
+q{CREATE TEMPORARY TABLE Groups (
+	id serial primary key,
+	Name varchar
+) }
+]
 }
 
 package TestApp::Employee;
@@ -292,4 +312,35 @@ sub NewItem {
 }
 
 
+package TestApp::Group;
+
+use base $ENV{SB_TEST_CACHABLE}?
+    qw/DBIx::SearchBuilder::Record::Cachable/:
+    qw/DBIx::SearchBuilder::Record/;
+
+sub Table { 'Groups' }
+
+sub Schema {
+    return {
+        Name => { TYPE => 'varchar' },
+    }
+}
+
+package TestApp::GroupCollection;
+
+use base qw/DBIx::SearchBuilder/;
+
+sub Table {
+    my $self = shift;
+    my $tab = $self->NewItem->Table();
+    return $tab;
+}
+
+sub NewItem {
+    my $self = shift;
+    my $class = 'TestApp::Group';
+    return $class->new( $self->_Handle );
+
+}
+
 1;

commit 2b487b024b86b40c30eb3e5db977c96bc8ab5bff
Merge: 5a6aa79 b053f35
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Jan 21 03:48:49 2021 +0800

    Merge branch 'optionally-quote-table-names'


commit 9390ab2468cc5ea3d2b52414e5d9e2aae07730d2
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Jan 21 03:56:01 2021 +0800

    Prep version 1.69

diff --git a/Changes b/Changes
index 35316a7..39b3120 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,7 @@
 Revision history for Perl extension DBIx::SearchBuilder.
 
+1.69 2021-01-20
+
  - New option to quote tablenames in queries, enabled automatically for MySQL 8
  - Updated tests for new MySQL 8 reserved words and tablename quoting
 
diff --git a/MANIFEST b/MANIFEST
index 4ae2e78..5d15017 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -56,6 +56,7 @@ t/03cud_from_select.t
 t/03rebless.t
 t/03transactions.t
 t/03versions.t
+t/04mysql_identifier_quoting.t
 t/10schema.t
 t/11schema_records.t
 t/20set_edge_cases.t
diff --git a/META.yml b/META.yml
index a36660f..683f1c0 100644
--- a/META.yml
+++ b/META.yml
@@ -35,4 +35,4 @@ requires:
   capitalization: '0.03'
 resources:
   license: http://dev.perl.org/licenses/
-version: '1.68'
+version: '1.69'
diff --git a/lib/DBIx/SearchBuilder.pm b/lib/DBIx/SearchBuilder.pm
index e9b9326..2356f3b 100755
--- a/lib/DBIx/SearchBuilder.pm
+++ b/lib/DBIx/SearchBuilder.pm
@@ -4,7 +4,7 @@ package DBIx::SearchBuilder;
 use strict;
 use warnings;
 
-our $VERSION = "1.68";
+our $VERSION = "1.69";
 
 use Clone qw();
 use Encode qw();

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list