[DBD::Pg] Implement pg_placeholder_nocolons rt#95173

dbdpg-commits-gENoVmVU/[email protected] Thu, 8 May 2014 21:55:16 +0000
Newsgroups gmane.comp.db.postgresql.dbdpg.cvs
Message-ID <[email protected]>
Committed by Graham Ollis <[email protected]>

Implement pg_placeholder_nocolons rt#95173

---
 Pg.pm    | 28 ++++++++++++++++++++++++++++
 dbdimp.c | 35 ++++++++++++++++++++++++++++++++++-
 dbdimp.h |  2 ++
 3 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/Pg.pm b/Pg.pm
index 1dc8971..68e2772 100644
--- a/Pg.pm
+++ b/Pg.pm
@@ -1679,6 +1679,7 @@ use 5.008001;
 				pg_pass                        => undef,
 				pg_pid                         => undef,
 				pg_placeholder_dollaronly      => undef,
+				pg_placeholder_nocolons        => undef,
 				pg_port                        => undef,
 				pg_prepare_now                 => undef,
 				pg_protocol                    => undef,
@@ -1736,6 +1737,7 @@ use 5.008001;
 				pg_cmd_status             => undef,
 				pg_oid_status             => undef,
 				pg_placeholder_dollaronly => undef,
+				pg_placeholder_nocolons   => undef,
 				pg_prepare_name           => undef,
 				pg_prepare_now            => undef,
 				pg_segments               => undef,
@@ -2604,6 +2606,20 @@ Alternatively, you can set it at prepare time:
     {pg_placeholder_dollaronly => 1});
   $sth->execute('segname');
 
+If your queries use array slices but you still want to use question marks as
+placeholders, you can tell DBD::Pg to ignore just colon placeholders by setting
+the L</pg_placeholder_nocolons> attribute in the same way. Examples:
+
+  $dbh->{pg_placeholder_nocolons} = 1;
+  $sth = $dbh->prepare(q{SELECT array[1:2] FROM mytable WHERE id = ?});
+  $sth->execute(1);
+
+Again, you may set it param time as well:
+
+  $sth = $dbh->prepare(q{SELECT array[1:2] FROM mytable WHERE id = ?}.
+    {pg_placeholder_nocolons => 1});
+  $sth->execute(1);
+
 =head3 B<prepare_cached>
 
   $sth = $dbh->prepare_cached($statement, \%attr);
@@ -3203,6 +3219,12 @@ DBD::Pg specific attribute. Defaults to false. When true, question marks inside
 are not treated as L<placeholders|/Placeholders>. Useful for statements that contain unquoted question 
 marks, such as geometric operators.
 
+=head3 B<pg_placeholder_nocolons> (boolean)
+
+DBD::Pg specific attribute. Defaults to false. When true, colons inside of statements
+are not treated as L<placeholders|/Placeholders>. Useful for statements that contain an
+array slice.
+
 =head3 B<pg_enable_utf8> (integer)
 
 DBD::Pg specific attribute. The behavior of DBD::Pg with regards to this flag has 
@@ -3870,6 +3892,12 @@ DBD::Pg specific attribute. Defaults to off. When true, question marks inside of
 being prepared are not treated as placeholders. Useful for statements that contain unquoted question 
 marks, such as geometric operators.
 
+=head3 B<pg_placeholder_nocolons> (boolean)
+
+DBD::Pg specific attribute. Defaults to off. When true, colons inside of statements
+are not treated as L<placeholders|/Placeholders>. Useful for statements that contain an
+array slice.
+
 =head3 B<pg_async> (integer)
 
 DBD::Pg specific attribute. Indicates the current behavior for asynchronous queries. See the section 
diff --git a/dbdimp.c b/dbdimp.c
index e55dd83..bfc8a44 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -236,6 +236,7 @@ int dbd_db_login6 (SV * dbh, imp_dbh_t * imp_dbh, char * dbname, char * uid, cha
  	imp_dbh->prepare_now       = DBDPG_FALSE;
 	imp_dbh->done_begin        = DBDPG_FALSE;
 	imp_dbh->dollaronly        = DBDPG_FALSE;
+	imp_dbh->nocolons          = DBDPG_FALSE;
 	imp_dbh->expand_array      = DBDPG_TRUE;
 	imp_dbh->txn_read_only     = DBDPG_FALSE;
 	imp_dbh->pid_number        = getpid();
@@ -791,6 +792,12 @@ SV * dbd_db_FETCH_attrib (SV * dbh, imp_dbh_t * imp_dbh, SV * keysv)
 			retsv = newSViv((IV)imp_dbh->switch_prepared);
 		break;
 
+	case 23: /* pg_placeholder_nocolons */
+
+		if (strEQ("pg_placeholder_nocolons", key))
+			retsv = newSViv((IV)imp_dbh->nocolons);
+		break;
+
 	case 25: /* pg_placeholder_dollaronly */
 
 		if (strEQ("pg_placeholder_dollaronly", key))
@@ -954,6 +961,14 @@ int dbd_db_STORE_attrib (SV * dbh, imp_dbh_t * imp_dbh, SV * keysv, SV * valuesv
 		}
 		break;
 
+	case 23: /* pg_placeholder_nocolons */
+
+		if (strEQ("pg_placeholder_nocolons", key)) {
+			imp_dbh->nocolons = newval ? DBDPG_TRUE : DBDPG_FALSE;
+			retval = 1;
+		}
+		break;
+
 	case 25: /* pg_placeholder_dollaronly */
 
 		if (strEQ("pg_placeholder_dollaronly", key)) {
@@ -1113,6 +1128,12 @@ SV * dbd_st_FETCH_attrib (SV * sth, imp_sth_t * imp_sth, SV * keysv)
 			retsv = newSViv((IV)imp_sth->switch_prepared);
 		break;
 
+	case 23: /* pg_placeholder_nocolons */
+
+		if (strEQ("pg_placeholder_nocolons", key))
+			retsv = newSViv((IV)imp_sth->nocolons);
+		break;
+
 	case 25: /* pg_placeholder_dollaronly */
 
 		if (strEQ("pg_placeholder_dollaronly", key))
@@ -1389,6 +1410,14 @@ int dbd_st_STORE_attrib (SV * sth, imp_sth_t * imp_sth, SV * keysv, SV * valuesv
 		}
 		break;
 
+	case 23: /* pg_placeholder_nocolons */
+
+		if (strEQ("pg_placeholder_nocolons", key)) {
+			imp_sth->nocolons = SvTRUE(valuesv) ? DBDPG_TRUE : DBDPG_FALSE;
+			retval = 1;
+		}
+		break;
+
 	case 25: /* pg_placeholder_dollaronly */
 
 		if (strEQ("pg_placeholder_dollaronly", key)) {
@@ -1549,6 +1578,7 @@ int dbd_st_prepare (SV * sth, imp_sth_t * imp_sth, char * statement, SV * attrib
 	imp_sth->switch_prepared  = imp_dbh->switch_prepared;
 	imp_sth->prepare_now      = imp_dbh->prepare_now;
 	imp_sth->dollaronly       = imp_dbh->dollaronly;
+	imp_sth->nocolons         = imp_dbh->nocolons;
 
 	/* Parse and set any attributes passed in */
 	if (attribs) {
@@ -1565,6 +1595,9 @@ int dbd_st_prepare (SV * sth, imp_sth_t * imp_sth, char * statement, SV * attrib
 		if ((svp = hv_fetch((HV*)SvRV(attribs),"pg_placeholder_dollaronly", 25, 0)) != NULL) {
 			imp_sth->dollaronly = SvTRUE(*svp) ? DBDPG_TRUE : DBDPG_FALSE;
 		}
+		if ((svp = hv_fetch((HV*)SvRV(attribs),"pg_placeholder_nocolons", 23, 0)) != NULL) {
+			imp_sth->nocolons = SvTRUE(*svp) ? DBDPG_TRUE : DBDPG_FALSE;
+		}
 		if ((svp = hv_fetch((HV*)SvRV(attribs),"pg_async", 8, 0)) != NULL) {
 			imp_sth->async_flag = (int)SvIV(*svp);
 		}
@@ -1950,7 +1983,7 @@ static void pg_st_split_statement (pTHX_ imp_sth_t * imp_sth, int version, char
 				placeholder_type = 1;
 			}
 			/* Colon style, but skip two colons in a row (e.g. myval::float) */
-			else if (':' == ch) {
+			else if (':' == ch && ! imp_sth->nocolons) {
 				if (':' == *statement) {
 					/* Might as well skip _all_ consecutive colons */
 					while(':' == *statement) {
diff --git a/dbdimp.h b/dbdimp.h
index 22d55e7..350f1a3 100644
--- a/dbdimp.h
+++ b/dbdimp.h
@@ -36,6 +36,7 @@ struct imp_dbh_st {
 	bool    prepare_now;       /* force immediate prepares, even with placeholders. Set by user, default is 0 */
 	bool    done_begin;        /* have we done a begin? (e.g. are we in a transaction?) */
 	bool    dollaronly;        /* only consider $1, $2 ... as valid placeholders */
+	bool    nocolons;          /* do not consider :1, :2 ... as valid placeholders */
 	bool    expand_array;      /* transform arrays from the db into Perl arrays? Default is 1 */
 	bool    txn_read_only;     /* are we in read-only mode? Set with $dbh->{ReadOnly} */
 
@@ -112,6 +113,7 @@ struct imp_sth_st {
 	bool   has_default;      /* does it have one or more 'DEFAULT' values? */
 	bool   has_current;      /* does it have one or more 'DEFAULT' values? */
 	bool   dollaronly;       /* Only use $1 as placeholders, allow all else */
+	bool   nocolons;         /* do not consider :1, :2 ... as valid placeholders */
 	bool   use_inout;        /* Any placeholders using inout? */
 	bool   all_bound;        /* Have all placeholders been bound? */
 };
-- 
1.8.4