Oracle Scratchpad

December 19, 2008

Updatable Join Views

Filed under: Infrastructure,Oracle,Troubleshooting — Jonathan Lewis @ 8:25 am GMT Dec 19,2008

I was in Slovenia earlier this week presenting one of the “Celebrity Seminars” for Oracle University. The audience was very good, and had some interesting questions and observations. One of them introduced me to the following oddity that he had discovered with updatable join views – a concept that is not commonly known but which can be the most efficient option for updating from one table to another.

Given suitable constraints, I can write a query that joins several tables, then turn that query into an inline view and update from one table to another, for example (tested on and

rem     Script:         upd_join_view.sql
rem     Author:         Jonathan Lewis
rem     Dated:          March 2006

update	(
		test_user.child		chi,
		test_user.parent	par,
		test_user.grandparent	gpa
		chi.small_num_c between 200 and 215
	and	par.id_gp = chi.id_gp
	and    = chi.id_p
	and    = par.id_gp
	)	gen
	gen.small_vc_c = gen.small_vc_gp

For this update to the child table to be legal, I have to be able to guarantee that any row from the table that appears in the inline view cannot logically appear there more than once.

This restriction is, in some ways, obvious: if a single row from the child table could appear twice in the join then the two appearances could correspond to two different rows from the grandparent table, which would leave Oracle trying to decide which grandparent value to use in the update.

Consider for example this data:

create table tabX (x1 number, x2 number);
create table tabY (y1 number, y2 number);

insert into tabx values(1,1);
insert into tabx values(1,99);
insert into taby values(1,-1);
insert into taby values(1,-99);


	y2, x2
	tabX, tabY
	tabx.x1 = taby.y1

        Y2         X2
---------- ----------
        -1         99
        -1          1
       -99         99
       -99          1

Both rows in the tabY table appear twice in the join result (as do both rows from tabX). If I try the following update, which value of x2 should end up overwriting the value for y2 in the underlying table – the 99 that happens to appear first in the join, or the 99 that happens to appear last (bearing in mind, of course, that the order of the output is not guaranteed) ?

update 	(
		y2, x2
		tabX, tabY
		tabx.x1 = taby.y1
	y2 = x2

ERROR at line 10:
ORA-01779: cannot modify a column which maps to a non key-preserved table

The technical term used by Oracle is “key-preserved”. Going back to the original example: if you identify a row in the child table using a unique key then the same key could act as a unique key to a table created by selecting all the rows from the join view. For this to work the join from child to parent has to be on a unique key in the parent table, and the join from the parent to grandparent has to be on a unique key in the grandparent table.

Finally we come to the oddity. You’ll notice that I had included the name of the table owner in my original query – this is because I want to be able to run the update from another schema called u1. So what privileges should test_user grant to u1 ? The obvious requirement would be as follows:

grant select, update (small_vc_c) on child to u1;
grant select                      on parent to u1;
grant select                      on grandparent to u1;

This means I can update the relevant column from the child table, and see all other columns from the childparent and grandparent tables that I need to do the update.

But when I try the update, I get the following error:

                test_user.grandparent   gpa
ERROR at line 8:
ORA-01031: insufficient privileges

Until I grant further (unexpected) privileges to u1, I can’t execute the update. This is what it takes:

grant update (id_gp, id_p, id, small_num_c) on child to u1;
grant update (id_gp, id)                    on parent to u1;
grant update (id, small_vc_gp)              on grandparent to u1;

I have to grant update column privileges of the relevant unique keys on all three tables and on the column from the grandparent table that I want to copy from. and on the column of the child table that appears in a filter predicate. This means that u1 has to have a level of update privilege that I don’t want it to have.

Of course, I can use non-deferrable referential integrity constraints to ensure that the key columns on the parent and grandparent tables can’t be changed – but I can’t stop u1 from changing the small_vc_gp column on the grandparent table, or the id or or small_num_c columns of the child table. I can’t think of a good reason why these extra privileges are needed.

There’s an important moral to this story. You should not build a system that allows end-users to connect to the database as the data owner.

But it’s still quite common for development work to take place in an environment that is a little lax about this rule, with developers connecting as the data owner to write code. This could leave you with code that seemed to work in the development environment but suddenly needs a change which grants excess privileges to end-users before it runs in the production environment.

Remember, it’s important to use a development environment that is a proper match for the production environment.

Update Aug 2019

The privilege situation hasn’t changed in Oracle  It’s worth mentioning, though, that by the optimizer is smart enough to eliminate the parent table from the plan (I haven’t checked



  1. Ever since 10g allowed one to use the MERGE statement to do a simple update (in other words, not requiring an INSERT portion of the statement), I have shied away from UPDATE JOINS, as they are really no longer required. Instead of relying on constraints to tell Oracle what you intend to update, MERGE allows you to specify it in the statement. That doesn’t get us around the issue in your test statement… but I think it makes coding a little easier.

    SQL> MERGE INTO taby t
     2  USING (SELECT x2,
     3                x1
     4            FROM tabx) s
     5  ON (s.x1=t.y1)
     8     SET t.y2 = s.x2;
    MERGE INTO taby t
    ERROR at line 1:
    ORA-30926: unable to get a stable set of rows in the source tables

    Elapsed: 00:00:00.01

    Comment by Stewart Bryson — December 19, 2008 @ 3:11 pm GMT Dec 19,2008 | Reply

    • Stewart,

      That’s a valid point – and the merge command can be very powerful – especially in 10g. Nevertheless there are three or four mechanisms for doing this type of thing, and each has its sttrengths and weaknesses. (Sometimes, for example, the best insert/update option is to write your own two-pass code using array exception handling).

      A minor drawback to the merge command is that you may not realise that a single incoming row can update multiple existing rows – a possibility that may be intuitively more obvious in the update join view. It is sometimes surprising how, on a subjective level, one technique can seem to be wonderful to one user and awful to another when, on an objective level, there is no significant difference.

      Comment by Jonathan Lewis — December 21, 2008 @ 8:07 pm GMT Dec 21,2008 | Reply

  2. @Jonathan

    The extra grants maybe “required” by the old bug (now solved) that permitted a user with only select privileges to update a view.

    I mean they can be an extra check or sort of.

    Comment by lascoltodelvenerdi — December 19, 2008 @ 4:13 pm GMT Dec 19,2008 | Reply

    • Lascoltodelvenerdi,

      Good point – it is sometimes quite surprising how a fix for one problem pops up some time later as an anomaly in some other situation.

      Comment by Jonathan Lewis — December 21, 2008 @ 8:09 pm GMT Dec 21,2008 | Reply

  3. I don’t think current view update vendor implementation should be taken seriously. After all even database theorists can’t agree how it should be done (except somewhat less ambitious approach with update triggers).

    How about the following scenario:

    drop table tabX;
    drop table tabY;

    create table tabX (p number,
    CONSTRAINT unique_xp UNIQUE (p)
    create table tabY (p number,
    CONSTRAINT unique_yp UNIQUE (p)

    insert into tabX values(1);
    insert into tabY values(1);


    insert into (
                   tabX x, tabY y
                   x.p = y.p
    ) values(2);

    select * from tabX;
    select * from tabY;
                   tabX x, tabY y
                   x.p = y.p;


    Now on my 11.1 db:
    1. After the update a row is inserted into tabX and not into tabY?
    2. The join view doesn’t change at all and show only one row??

    Comment by Vadim Tropashko — December 19, 2008 @ 10:53 pm GMT Dec 19,2008 | Reply

  4. Experts,

    is it possible to parallelize the actual update statement while using Updatable Join View?

    alter session enable parallel dml;

    update /*+ PARALLEL(gen) */
                   test_user.child                chi,
                   test_user.parent        par,
                   test_user.grandparent        gpa
                   chi.small_num_c between 200 and 215
           and        par.id_gp = chi.id_gp
           and    = chi.id_p
           and    = par.id_gp
    ) gen
           gen.small_vc_c = gen.small_vc_gp

    the above does not work :( the actual update is done in single thread.
    2 days ago at work we wanted to update 30 mil. of rows in a 80 mil. table from another table.
    While we could parallelize query(HJ) in a updateable view (which we verified could complete in a couple of minutes without the update statement) longops with update statement showed 2 days to complete.
    The solution was to use parallel CTAS to rebuild the whole table, which completed in under 45 mins, indexes included.


    Comment by Dan — December 19, 2008 @ 11:17 pm GMT Dec 19,2008 | Reply

    • Dan,

      I’ve edited your code to be more readable – which included changing the hint syntax because the “double hyphen” version didn’t show up properly.

      I am puzzled by the results of testing on My plan looks as if the update is able to run parallel, v$pq_tqstat reports data that says the update actually ran parallel, v$transaction shows parallel generation of redo – yet I don’t get the usual Oracle error ORA-12838: cannot read/modify an object after modifying it in parallel when I tried to do a second update without issuing a commit against the first.

      This was the execution plan for one test:

      | Id  | Operation                           | Name        | Rows  | Time     |    TQ  |IN-OUT| PQ Distrib |
      |   0 | UPDATE STATEMENT                    |             |   335 | 00:00:14 |        |      |            |
      |   1 |  PX COORDINATOR                     |             |       |          |        |      |            |
      |   2 |   PX SEND QC (RANDOM)               | :TQ10003    |     1 | 00:00:01 |  Q1,03 | P->S | QC (RAND)  |
      |   3 |    INDEX MAINTENANCE                | CHILD       |       |          |  Q1,03 | PCWP |            |
      |   4 |     PX RECEIVE                      |             |     1 | 00:00:01 |  Q1,03 | PCWP |            |
      |   5 |      PX SEND RANGE                  | :TQ10002    |     1 | 00:00:01 |  Q1,02 | P->P | RANGE      |
      |   6 |       UPDATE                        | CHILD       |       |          |  Q1,02 | PCWP |            |
      |   7 |        PX RECEIVE                   |             |     1 | 00:00:01 |  Q1,02 | PCWP |            |
      |   8 |         PX SEND HASH (BLOCK ADDRESS)| :TQ10001    |     1 | 00:00:01 |  Q1,01 | P->P | HASH (BLOCK|
      |*  9 |          TABLE ACCESS BY INDEX ROWID| CHILD       |     1 | 00:00:01 |  Q1,01 | PCWC |            |
      |  10 |           NESTED LOOPS              |             |   335 | 00:00:14 |  Q1,01 | PCWP |            |
      |* 11 |            HASH JOIN                |             |  2000 | 00:00:01 |  Q1,01 | PCWP |            |
      |  12 |             PX BLOCK ITERATOR       |             |  1000 | 00:00:01 |  Q1,01 | PCWC |            |
      |  13 |              TABLE ACCESS FULL      | GRANDPARENT |  1000 | 00:00:01 |  Q1,01 | PCWP |            |
      |  14 |             BUFFER SORT             |             |       |          |  Q1,01 | PCWC |            |
      |  15 |              PX RECEIVE             |             |  2000 | 00:00:01 |  Q1,01 | PCWP |            |
      |  16 |               PX SEND BROADCAST     | :TQ10000    |  2000 | 00:00:01 |        | S->P | BROADCAST  |
      |  17 |                INDEX FAST FULL SCAN | P_PK        |  2000 | 00:00:01 |        |      |            |
      |* 18 |            INDEX RANGE SCAN         | C_PK        |     1 | 00:00:01 |  Q1,01 | PCWP |            |

      Comment by Jonathan Lewis — December 21, 2008 @ 9:31 pm GMT Dec 21,2008 | Reply

  5. @Vadim

    Where is the problem?

    I think it work correctly.

    1. After the update a row is inserted into tabX and not into tabY?

    That’s right! The insert as the update of a view, can work only on ONE table.
    If you want to insert rows into more than one table, you must use the multi-table insert.

    2. The join view doesn’t change at all and show only one row??
    Right. As you inserted only in one table, the join condition “x.p = y.p” is not satisfied.


    Last hope: a fully qualified hint

    update /*+ PARALLEL(gen.chi,4) */

    so forcing the degree also.

    But I’m not sure it will work…

    I would try to put the parallel hint also in the select.

    Comment by lascoltodelvenerdi — December 20, 2008 @ 8:02 pm GMT Dec 20,2008 | Reply

    • lascoltodelvenerdi,

      Relational database theory says that, in principle, you should not be able to tell whether your SQL is accessing a a table or a view – so I should be able to insert a row into the view and then be able to see it when I query the view (Chris Date has some very good material in his presentations about this).

      In Vadim’s example, he’s inserted a row into a view, and simply doesn’t exist when he queries that view.

      Comment by Jonathan Lewis — December 21, 2008 @ 9:37 pm GMT Dec 21,2008 | Reply

  6. “then be able to see it when I query the view ”
    When you create an explicit view you can add the “WITH CHECK OPTION” clause so that you can’t insert a row that you couldn’t subsequently query. Have to see what that does for vadim’s example

    Comment by Gary — December 21, 2008 @ 10:06 pm GMT Dec 21,2008 | Reply

  7. @Jonathan

    I get the point, but looking at how Oracle works with updatable views, I think that the result of Vadim is OK.

    For sure it is not an optimal implementation, but is the only that we got (in Oracle at least).

    Comment by lascoltodelvenerdi — December 23, 2008 @ 9:08 am GMT Dec 23,2008 | Reply

  8. Finally got around to checking Vadim’s view from comment 3 in 11g.
    Using an explicit view, rather than in-line view, it works as stated.
    If I add the WITH CHECK OPTION clause I mentioned in comment 10, the view becomes non-updatable.

    Comment by Gary — December 31, 2008 @ 3:19 am GMT Dec 31,2008 | Reply

  9. Even though the Update statement runs in parallel we saw lot of “db file sequential eads” wait events even though we are updating columns which have no indexes.

    What could be the reason for that,can you please let us know.


    Comment by Vikas — July 26, 2009 @ 5:59 am BST Jul 26,2009 | Reply

    • I bet it’s single block reads of the table which is updated – because to update a row by ROWID you have to read this block to buffer cache. When PX is in use, there’s a big chance a block wouldn’t be in cache, so you have to read it.

      Comment by Timur Akhmadeev — July 26, 2009 @ 9:02 am BST Jul 26,2009 | Reply

  10. Vikas,

    Parallel execution doesn’t stop indexed access paths appearing in the execution plan. (See this note, for example). And even if you do nothing by direct path reads to get the raw data, you may have to do single block reads on the undo segment to create read-consistent copies of some of the data blocks.

    Comment by Jonathan Lewis — July 26, 2009 @ 6:48 am BST Jul 26,2009 | Reply

  11. Hi Jonathan,

    Thanks for clarifying some of the points. However if you could explain me a liitle bit more on the updateable join views it would definitely help.

          update /*+ parallel (x) nologging */ 
            select /*+ parallel(a) parallel(b) use_hash (b a) */
            b.crtv_name b_crtv_name,
            b.pending_flg b_pending_flg,
            b.edit_status b_edit_status,
            b.exploded_flg b_exploded_flg,
            b.ss_rank b_ss_rank,
            b.cm_rank b_cm_rank,
            b.edit_subm_tmstmp b_edit_subm_tmstmp,
            b.edit_revw_tmstmp b_edit_revw_tmstmp,
            b.del_tmstmp b_del_tmstmp,
            b.lang_id b_lang_id,
            b.url_id b_url_id,
            b.title b_title,
            b.descr b_descr,
            b.short_descr b_short_descr,
            b.display_url b_display_url,
            b.status b_status,
            b.ss_weight b_ss_weight,
            b.cm_weight b_cm_weight,
            b.ss_rotation_flg b_ss_rotation_flg,
            b.cm_rotation_flg b_cm_rotation_flg,
            b.crtv_rating b_crtv_rating,
            b.historical_crtv_sid b_historical_crtv_sid,
            decode(b.ir_dml_type, '2', trunc(sysdate), a.effective_start_date) b_effective_start_date
            from ysmcd.dim_com_creative_c a,
            ysmcd.dim_stg_creative b
            where a.crtv_sid = b.crtv_id
            and b.ir_dml_type in ('1','2')
          ) x
          crtv_name = b_crtv_name,
          crtv_pend_flag = b_pending_flg,
          crtv_edit_status = b_edit_status,
          crtv_exploded_flag = b_exploded_flg,
          ss_rank = b_ss_rank,
          cm_rank = b_cm_rank,
          crtv_edit_subm_tmstmp = b_edit_subm_tmstmp,
          crtv_edit_revw_tmstmp = b_edit_revw_tmstmp,
          crtv_del_tmstmp = b_del_tmstmp,
          crtv_lang_id = b_lang_id,
          crtv_url_key = b_url_id,
          crtv_title = b_title,
          crtv_desc = b_descr,
          crtv_short_desc = b_short_descr,
          crtv_display_url = b_display_url,
          crtv_status = b_status,
          crtv_ss_weight = b_ss_weight,
          crtv_cm_weight = b_cm_weight,
          crtv_ss_rotation_flg = b_ss_rotation_flg,
          crtv_cm_rotation_flg = b_cm_rotation_flg,
          crtv_rating = b_crtv_rating,
          historical_crtv_sid = b_historical_crtv_sid,
          effective_start_date = b_effective_start_date;
    SQL> set linesize 1000
    SQL> select * from table(dbms_xplan.display);
    Plan hash value: 4193413443
    | Id  | Operation                    | Name               | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |    TQ  |IN-OUT| PQ Distrib |
    |   0 | UPDATE STATEMENT             |                    |  3220K|  1824M| 23783   (1)| 00:07:09 |    |          |        |      |            |
    |   1 |  UPDATE                      | DIM_COM_CREATIVE_C |       |       |            |          |    |          |        |      |            |
    |   2 |   PX COORDINATOR             |                    |       |       |            |          |    |          |        |      |            |
    |   3 |    PX SEND QC (RANDOM)       | :TQ10001           |  3220K|  1824M| 23783   (1)| 00:07:09 |    |          |  Q1,01 | P->S | QC (RAND)  |
    |*  4 |     HASH JOIN                |                    |  3220K|  1824M| 23783   (1)| 00:07:09 |    |          |  Q1,01 | PCWP |            |
    |   5 |      PX RECEIVE              |                    |  3220K|   826M|   699   (1)| 00:00:13 |    |          |  Q1,01 | PCWP |            |
    |   6 |       PX SEND BROADCAST LOCAL| :TQ10000           |  3220K|   826M|   699   (1)| 00:00:13 |    |          |  Q1,00 | P->P | BCST LOCAL |
    |   7 |        PX BLOCK ITERATOR     |                    |  3220K|   826M|   699   (1)| 00:00:13 |  1 |    64    |  Q1,00 | PCWC |            |
    |*  8 |         TABLE ACCESS FULL    | DIM_STG_CREATIVE   |  3220K|   826M|   699   (1)| 00:00:13 |  1 |    64    |  Q1,00 | PCWP |            |
    |   9 |      PX BLOCK ITERATOR       |                    |   309M|    93G| 23067   (1)| 00:06:56 |  1 |    64    |  Q1,01 | PCWC |            |
    |  10 |       TABLE ACCESS FULL      | DIM_COM_CREATIVE_C |   309M|    93G| 23067   (1)| 00:06:56 |  1 |    64    |  Q1,01 | PCWP |            |
    Predicate Information (identified by operation id):
       4 - access("A"."CRTV_SID"="B"."CRTV_ID")
       8 - filter("B"."IR_DML_TYPE"='1' OR "B"."IR_DML_TYPE"='2')

    During the run of this update we have continously monitored that the inner join is doing a HASH JOIN, with DIM_COM_CREATIVE_C doing Rowid Range Scan by “n” parallel slaves. We can see the direct path read events during its run which is obvious since the data is getting hash probed in DIM_STG_CREATIVE which is hashed.

    My questions:

    1) Is this statement doing Parallel Updates, I see P->S operation during the last step.
    2) At which step of the execution plan does it know which blocks need updation and have to be sent to buffer cache using single block I/O access. I am seeing this event even when Parallel Slaves are doing a direct path read. should it not be the last step when the BULK UPDATE is going to happen.
    3) I am seeing the direct path reads along with db file sequential reads happening during this update run process.

    Please let me understand how it works.


    Comment by Vikas — July 27, 2009 @ 4:03 am BST Jul 27,2009 | Reply

    • 1) No. PDML must be explicitly enabled in the session:

      alter session enable parallel dml;

      And there are limitations to PDML.
      2) At the #1 step update takes place. Rows are sent to QC at the step #3. As you are using partial partition-wise join with broadcasting DIM_STG_CREATIVE table, Oracle is able to pipe all steps and run them “simultaneously”. Here is a note by Jonathan about HJ with PX when HASH-HASH distribution is in use – in this case you won’t see update before HJ is completely finished.
      3) Again – table and undo blocks have to be read to perform update

      You should read documentation about PX and plans to get better understanding what’s going on:
      Using Parallel Execution,

      Comment by Timur Akhmadeev — July 27, 2009 @ 7:13 am BST Jul 27,2009 | Reply

    • Vikas,
      I think Timur has addressed all your points.

      Comment by Jonathan Lewis — July 27, 2009 @ 8:18 pm BST Jul 27,2009 | Reply

  12. […] view because it didn’t recognise that a particular type of aggregate subquery would guarantee key preservation.  Here’s another example where the human eye can see key-preservation, but the optimizer […]

    Pingback by I wish | Oracle Scratchpad — August 19, 2019 @ 1:41 pm BST Aug 19,2019 | Reply

  13. […] I’d written about the merge command and “stable sets”, and got to a draft about updatable join views that I’d started in 2016 in response to a question on OTN (as it was at the time) and finally […]

    Pingback by Join View | Oracle Scratchpad — August 20, 2019 @ 12:39 pm BST Aug 20,2019 | Reply

  14. Below code run on Oracle live SQL – Oracle Database 19c Enterprise Edition Release – Production

    create table grandparent 
      id number primary key, 
      small_vc_gp varchar2(5) 
    create table parent 
      id_gp number, 
      id number, 
      small_vc_p varchar2(5),  
      constraint par_FK1 foreign key (id_gp) references grandparent(id), 
      constraint par_pk primary key (id_gp, id) 
    create table child 
      id_gp number, 
      id_par number, 
      id number, 
      small_vc_c varchar2(5), 
      constraint ch_fk2 foreign key(id_gp, id_par) references parent(id_gp, id), 
      constraint ch_pk primary key (id_gp, id_par, id) 
    insert into grandparent values (1,'GP1');
    insert into parent values (1,1,'P1');
    insert into parent values (1,2,'P2');
    insert into child values (1,1,1,'C1');
    insert into child values (1,2,1,'C1');
    --this works
        select c.small_vc_c, gp.small_vc_gp 
        from child c join parent p 
        on and c.id_gp=p.id_gp 
        join grandparent gp 
    set v.small_vc_c = v.small_vc_gp 
    ---this also work
        select c.small_vc_c, p.small_vc_p 
        from child c join parent p 
        on and c.id_gp=p.id_gp 
        join grandparent gp 
    set v.small_vc_c = v.small_vc_p;
    --this doesn't work updating parent table
        select p.small_vc_p, gp.small_vc_gp 
        from child c join parent p 
        on and c.id_gp=p.id_gp 
        join grandparent gp 
    set v.small_vc_p = v.small_vc_gp;
    ORA-01779: cannot modify a column which maps to a non key-preserved table 

    To me rows from parent is key preseve or I miss something?

            child c 
            parent p
    and     c.id_gp=p.id_gp
            grandparent gp
         ID_GP     ID_PAR         ID SMALL      ID_GP         ID SMALL         ID SMALL
    ---------- ---------- ---------- ----- ---------- ---------- ----- ---------- -----
             1          1          1 P1             1          1 P1             1 GP1
             1          1          2 P1             1          1 P1             1 GP1

    Thank you

    Comment by Henish — August 22, 2019 @ 8:51 pm BST Aug 22,2019 | Reply

    • Henish,

      In theory it should be possible to update from grandparent to parent in this case even though parent is NOT key-preserved.

      To see that parent is not key preserved simply change the insert into child to give each parent two child rows:

      insert into child values (1,1,1,'C11');
      insert into child values (1,1,2,'C12');
      insert into child values (1,2,1,'C21');
      insert into child values (1,2,2,'C22');

      If you do this your final query will show each parent key/row appearing twice.

      The reason I say “in theory it should be possible” is it is clear to the human eye that while each parent row is multiplied up by the number of its child rows, each parent will always appear with the same grandparent. However the optimizer / run-time engine is not coded to take that extra logical step.

      It’s worth noting that 12c the update would work if you added the clause: “where = 1”. (This is an enhancement that appeared in 12c, I have previously written about the “constant enforcing uniqueness” failure in 11g.

      Comment by Jonathan Lewis — August 23, 2019 @ 9:16 am BST Aug 23,2019 | Reply

RSS feed for comments on this post. TrackBack URI

Comments and related questions are welcome.

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Website Powered by